# JavaScript Tracking

> Source: https://docs.trailspark.ai/webhooks-api/javascript-tracking

## Overview

Add event tracking to your website or product without Segment or a CDP. The Trailspark tracking script is a lightweight JavaScript library that sends signals directly to your webhook endpoint. It supports `track`, `identify`, and `page` calls with automatic anonymous identity resolution.

## Quick Start

Add the script tag to your site with your API key:

```html
<script src="https://app.trailspark.ai/t.js" data-key="YOUR_API_KEY"></script>
<script>
  trailspark.page();
</script>
```

Create an API key at **Settings** > **API Keys** with endpoint type **Signal Staging**. Enable the **Enable client-side tracking** toggle and optionally configure allowed domains. See [Managing API Keys](https://docs.trailspark.ai/webhooks-api/api-keys).

> [!WARNING]
> For browser-based tracking, create your API key **without a secret**. Secrets are visible in client-side JavaScript and offer no security benefit in this context. The API key is write-only — it can only send signals, not read any data.

## Installation

### Script Tag (recommended)

The simplest option. The `data-key` attribute auto-initializes the library:

```html
<script src="https://app.trailspark.ai/t.js" data-key="YOUR_API_KEY"></script>
```

### Async Loading

Load the script without blocking page rendering. Commands called before the script loads are queued and replayed automatically:

```html
<script>
  !function(){var t=window.trailspark={_q:[],
  init:function(){t._q.push(["init"].concat([].slice.call(arguments)))},
  track:function(){t._q.push(["track"].concat([].slice.call(arguments)))},
  identify:function(){t._q.push(["identify"].concat([].slice.call(arguments)))},
  page:function(){t._q.push(["page"].concat([].slice.call(arguments)))},
  reset:function(){t._q.push(["reset"].concat([].slice.call(arguments)))},
  getAnonymousId:function(){return null}};
  var s=document.createElement("script");
  s.async=true;s.src="https://app.trailspark.ai/t.js";
  s.setAttribute("data-key","YOUR_API_KEY");
  document.head.appendChild(s)}();
</script>
```

### Manual Initialization

If you need to set a custom host or delay initialization:

```html
<script src="https://app.trailspark.ai/t.js"></script>
<script>
  trailspark.init('YOUR_API_KEY', {
    host: 'https://app.trailspark.ai'
  });
</script>
```

The `host` option defaults to the origin of the script URL. You only need to set it if your Trailspark instance runs at a different address than where the script is served.

## API Reference

### track(event, properties)

Record a user action.

| Parameter | Type | Description |
| - | - | - |
| **event** | string | Event name (e.g., `"Form Submitted"`, `"Feature Activated"`) |
| **properties** | object | Optional event data |

```javascript
trailspark.track('Demo Requested', {
  form_id: 'hero-demo-form',
  plan_interest: 'enterprise'
});
```

### identify(traits)

Link the current anonymous visitor to a known identity. Call this at login, signup, or any point where you know the user's email.

| Parameter | Type | Description |
| - | - | - |
| **traits** | object | User attributes. Include `email` for identity resolution |

```javascript
trailspark.identify({
  email: 'user@company.com',
  name: 'Jane Smith',
  company: 'Acme Corp',
  title: 'VP Engineering'
});
```

After an `identify` call, Trailspark links all prior anonymous signals from this visitor to the newly identified lead. See [Identity Resolution](https://docs.trailspark.ai/signal-management/identity-resolution).

### page(name, properties)

Record a page view. Automatically captures `url`, `path`, `referrer`, `title`, and `search`.

| Parameter | Type | Description |
| - | - | - |
| **name** | string | Optional page name |
| **properties** | object | Optional additional properties (merged with auto-captured values) |

```javascript
trailspark.page();

trailspark.page('Pricing', { plan_shown: 'enterprise' });
```

### reset()

Clear the current identity. Call on logout to prevent the next user's activity from being attributed to the previous user. Generates a new anonymous ID.

```javascript
trailspark.reset();
```

### getAnonymousId()

Returns the current anonymous ID (persisted in localStorage).

```javascript
var anonId = trailspark.getAnonymousId();
```

## Implementation Patterns

### Page View Tracking

Track views on every page load:

```html
<script src="https://app.trailspark.ai/t.js" data-key="YOUR_API_KEY"></script>
<script>
  trailspark.page();
</script>
```

For single-page applications, call `trailspark.page()` on route changes:

```javascript
// React Router example
useEffect(() => {
  trailspark.page();
}, [location.pathname]);
```

> [!TIP]
> Not every page view adds signal value. Consider tracking only high-intent pages like pricing, case studies, and documentation rather than every route.

### Form Submissions

Track form submissions that indicate buying intent:

```javascript
document.getElementById('demo-form').addEventListener('submit', function (e) {
  trailspark.track('Demo Requested', {
    form_id: 'demo-form',
    company: document.getElementById('company').value,
    company_size: document.getElementById('size').value
  });
});
```

### Login and Signup

Identify users at authentication to link their anonymous browsing history:

```javascript
// After successful login or signup
function onAuthSuccess(user) {
  trailspark.identify({
    email: user.email,
    name: user.name,
    company: user.company,
    userId: user.id
  });
}
```

### Product Events

Track feature usage and milestones inside your product:

```javascript
trailspark.track('Project Created', {
  project_type: 'team',
  template: 'kanban'
});

trailspark.track('Team Member Invited', {
  invite_count: 3,
  role: 'editor'
});

trailspark.track('Integration Connected', {
  integration: 'slack'
});
```

### Logout

Reset identity when users log out:

```javascript
function onLogout() {
  trailspark.reset();
}
```

## How It Works

1. When the script loads, it generates a unique `anonymousId` and stores it in localStorage
2. All `track` and `page` calls include this `anonymousId` in the payload
3. Signals from anonymous visitors are stored in cold storage
4. When `identify` is called with an email, Trailspark links the `anonymousId` to the identified lead
5. All prior anonymous signals are rehydrated and associated with that lead

This means you can start tracking immediately — even before you know who the visitor is. Their full activity history becomes available once they identify themselves.

For more details, see [Identity Resolution](https://docs.trailspark.ai/signal-management/identity-resolution).

## Domain Restrictions

Each API key can be restricted to specific domains. When allowed domains are configured, only requests from those origins will be accepted — requests from any other website will be rejected with a 403 error.

To configure allowed domains, go to **Settings** > **API Keys**:

- **New keys**: Enable the **Enable client-side tracking** toggle when creating a key. Enter the full origins (e.g., `https://example.com`, `https://app.example.com`) as a comma-separated list. Leave the field empty to allow all origins.
- **Existing keys**: Click the pencil icon next to "Allowed domains" on any active key card, or click **+ Add allowed domains** if none are configured yet.

> [!WARNING]
> For browser-based tracking, create your API key **without a secret**. Secrets are visible in client-side JavaScript and offer no security benefit in this context. Use allowed domains instead to restrict which sites can send signals.

> [!NOTE]
> Domain restrictions only apply to browser-based requests that include an `Origin` header. Server-to-server webhook calls (which do not send an `Origin` header) are unaffected and always allowed.

## First-Party Domain (CNAME)

Ad blockers and privacy tools may block requests to third-party tracking domains. To avoid this, you can serve the tracking script and send signals from your own domain using a CNAME record.

### Setup

1. Create a DNS CNAME record pointing a subdomain to your Trailspark subdomain:

```
tracking.yoursite.com → acme.trailspark.ai
```

2. Configure SSL/TLS for the custom subdomain through your CDN or hosting provider (e.g., Cloudflare, AWS CloudFront)

3. Update your tracking script to use the first-party domain:

```html
<script src="https://tracking.yoursite.com/t.js" data-key="YOUR_API_KEY"></script>
```

The script auto-detects its host from the `src` attribute, so all signal requests will be sent through `tracking.yoursite.com` — appearing as first-party traffic to the browser.

## Troubleshooting

**Signals not appearing in Signal Queue** -- Open the browser's Network tab and look for POST requests to `/api/signal-staging/webhook/`. Check for HTTP error responses. Verify the API key is active and not expired at **Settings** > **API Keys**.

**Identity not resolving** -- The `identify` call must include an `email` field in traits. Without email, Trailspark cannot create a lead record. Confirm `identify` is being called after login or signup.

**Console errors about sendBeacon** -- The script automatically falls back to `fetch` if `sendBeacon` is unavailable. If both fail, check that the Trailspark endpoint is reachable from the user's browser.

**HTTP 429 responses** -- You've either exceeded the request rate limit, or gone past **twice** your plan's ingested-signal limit. Between the limit and twice the limit, signals are still accepted at no extra charge but held (paused signals don't update your scores, and they're deleted at the end of the billing period); only beyond that are they rejected. Reduce tracking volume by filtering to high-intent events only, enable usage overages, or upgrade your plan. That free extra room isn't permanent: on a paid plan, ending **three billing periods in a row** over the allowance without pay-as-you-go turned on removes it for good, after which signals past the allowance are rejected immediately. A period back inside the allowance resets the count.

## Next Steps

- [API Keys](https://docs.trailspark.ai/webhooks-api/api-keys) -- manage keys for the tracking endpoint
- [Webhook Payload Format](https://docs.trailspark.ai/webhooks-api/webhook-payload-format) -- full field reference for signal payloads
- [Creating Signal Mapping](https://docs.trailspark.ai/signal-management/creating-signal-mapping) -- set up rules to score incoming signals
- [Identity Resolution](https://docs.trailspark.ai/signal-management/identity-resolution) -- how anonymous tracking connects to leads
