# JavaScript Tracking

> Source: https://docs.trailspark.ai/docs/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](/docs/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](/docs/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](/docs/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** -- Your plan's signal limit has been reached. Reduce tracking volume by filtering to high-intent events only, or upgrade your plan.

## Next Steps

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