# Track funnel events with track and identify

> When to fire each Kalman event, the fields it accepts, what gets dropped, where to paste the calls and how to check they arrived.

Updated 2026-11-10 · Tracking · https://kalmananalytics.com/support/tracking-events/

Once the [Kalman tag is installed](/support/install-the-tag/), page views, click IDs and UTM tags are recorded automatically. This article covers the two calls you add yourself:

- `kalman('track', ...)` marks a funnel step, such as an intake starting or a purchase.
- `kalman('identify', ...)` links the visitor's browser to their later purchases, so ad platforms can match more of them.

## Do you need this?

It depends on where your purchases come from.

- **If you've connected [Sylaris OS](/support/connect-sylaris-os/) or a [webhook source](/support/webhook-catchers/):** intake, purchase, approval and refill events already arrive server-side. You usually only need `identify`, on the page where the patient enters their email.
- **If not:** add the `track` calls below for the steps your site handles.

The install page's **Track funnel steps** section tells you which case applies to your workspace.

## The events

| Event                   | When to fire it                                                     | Example                                                                                                    |
| ----------------------- | ------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- |
| `page_view`             | Automatic on every page load and in-app navigation. Nothing to add. | —                                                                                                          |
| `lead`                  | Someone leaves their details: a waitlist, quiz or contact form.     | `kalman('track', 'lead');`                                                                                 |
| `intake_started`        | The first step of the intake form is shown.                         | `kalman('track', 'intake_started');`                                                                       |
| `intake_step_completed` | A step of the intake is completed.                                  | `kalman('track', 'intake_step_completed', { step_index: 2, step_count: 6 });`                              |
| `intake_completed`      | The intake is submitted.                                            | `kalman('track', 'intake_completed');`                                                                     |
| `product_selected`      | The patient picks a plan or product.                                | `kalman('track', 'product_selected', { product_sku: 'plan-a' });`                                          |
| `checkout_started`      | The checkout page or payment step opens.                            | `kalman('track', 'checkout_started', { product_sku: 'plan-a', value: 199, currency: 'USD' });`             |
| `purchase`              | The payment succeeds (the confirmation page).                       | `kalman('track', 'purchase', { order_ref: orderId, product_sku: 'plan-a', value: 199, currency: 'USD' });` |
| `account_created`       | The patient creates a portal account.                               | `kalman('track', 'account_created');`                                                                      |
| `portal_viewed`         | The patient opens the portal after signing in.                      | `kalman('track', 'portal_viewed');`                                                                        |
| `custom`                | Anything else worth counting.                                       | `kalman('track', 'custom', { name: 'pricing_faq_opened' });`                                               |

In these examples, `orderId` stands for a variable your page already has. It isn't text to copy.

### Events the browser can't send

`provider_approved`, `provider_declined`, `order_shipped`, `refill_purchased`, `subscription_canceled` and `refund_issued` only come from your platform (Sylaris OS or a webhook source). If a page sends one, Kalman refuses it.

### Custom events

A custom name must start with a letter. It can use only letters, numbers and underscores, up to 40 characters. Kalman refuses any name that refers to a condition or medication, or to one of your own sensitive terms. Refused names show up as a count in your weekly privacy summary.

If you call `kalman('track', 'pricing_faq_opened')` with a name that isn't on the list, it's recorded as a custom event with that name.

## Fields Kalman accepts

For `purchase`, `checkout_started` and `product_selected`:

- `order_ref`: your order ID. Kalman uses it to match the server event for the same order, so a purchase is counted once. Letters, numbers and `- _ . :` only, up to 120 characters.
- `product_sku`: your SKU code, mapped to a product in Kalman (see [Products and aliases](/support/products-and-aliases/)). Use a code like `plan-a`, never a medication name. Up to 100 characters, with the same characters allowed as `order_ref`.
- `value`: the amount as a number, such as `199` or `199.50`. No currency symbol.
- `currency`: a 3-letter code, such as `USD`, `CAD` or `GBP`.
- `event_id` (optional): an ID shared with the Meta pixel for the same event, so Meta keeps one copy. See "Using the Meta pixel too" below.

For intake steps:

- `step_index` and `step_count`: whole numbers.
- `journey_key`: a short code saying which intake it is, such as `intake_v2`. Letters, numbers, `-` and `_`, up to 40 characters.

## What gets dropped

- The tag sends only the fields listed above. Anything else you pass is left out in the browser.
- A value in the wrong format is dropped and the rest of the event is kept. For example, a SKU with spaces, a value that isn't a number, or a currency that isn't 3 letters.
- **Never put names, conditions, medications, intake answers or free text in any field.**

## Link the visitor (identify)

Call `identify` once the patient has typed their email, for example when that form step is submitted:

```js
// On the step where the patient types their email, e.g. on submit:
kalman('identify', { email: emailInput.value });

// Phone too, if you collect it:
kalman('identify', { email: emailInput.value, phone: phoneInput.value });

// Your own patient or account ID, if you have one:
kalman('identify', { email: emailInput.value, external_id: accountId });
```

`emailInput.value`, `phoneInput.value` and `accountId` stand for values your form or page already holds. They aren't text to copy. Pass the variable, not a fixed value.

What happens:

- In the browser, the tag lowercases the email and keeps only the phone's digits. A 10-digit number is treated as a US number. It then hashes each value (SHA-256).
- Only the hashes reach Kalman. The plain email and phone never leave the browser.
- If none of the values is usable (no `@` in the email, too few digits in the phone), nothing is sent.
- Hashing needs a secure page, so `identify` works on `https` pages only.

## Using the Meta pixel too

If the Meta pixel fires the same event, give both the same ID so Meta counts it once:

```js
var id = kalman.eventId();
kalman('track', 'purchase', {
    event_id: id,
    order_ref: orderId,
    value: 199,
    currency: 'USD',
});
fbq('track', 'Purchase', {}, { eventID: id });
```

## Where to paste the calls

- **Custom site or intake:** call `kalman(...)` in your code where the step happens.
- **Webflow or Framer:** the page's custom code (before `</body>`) on that step's page.
- **WordPress:** a code snippet plugin, or your theme's footer.
- **Google Tag Manager:** a Custom HTML tag with the call inside a `<script>`, fired by a trigger on that step.

The [snippet](/support/install-the-tag/) must be on the page too.

## Check it worked

1. Do the step on your own site.
2. On the install page, open **Check it worked** and click **Events log for the last 10 minutes**. You can also open **Event log** in the sidebar.
3. Look for a row with the event name and the source **Browser tag**. For a purchase, check the **Value** and **Product** columns too.

The filter shows **Last 10 minutes · show all**. Click it to see everything. If nothing shows up, see [Troubleshooting: no data](/support/troubleshooting-no-data/).

## What Kalman never sends

Kalman builds what goes to ad platforms from a short allowlist. It never sends product names, SKUs, conditions, medications or intake answers. Email and phone only ever travel as hashes. Read more in [Event log and data flow](/support/events-log-and-data-flow/).

Still stuck? Log in and open a request: https://app.kalmananalytics.com/support
