Skip to main content

Overview

Beyond automatic page view tracking, the Tinybird tracker provides a JavaScript API for tracking custom events. Use this to capture:
  • User interactions - Clicks, form submissions, scrolls
  • Business events - Add to cart, purchases, sign-ups
  • Feature usage - Tool usage, settings changes
  • Custom metrics - Any domain-specific events

Basic Usage

Once the tracker script is loaded, use the global Tinybird.trackEvent() function:
Tinybird.trackEvent('event_name', {
  // Your custom properties
})

Function Signature

name
string
required
Event name identifier. Use descriptive, snake_case names.Examples: add_to_cart, purchase_complete, video_play
payload
object
required
Event properties as a JavaScript object. Can include any custom data relevant to your event.
Payload must be between 2 and 10,240 bytes when stringified.

Examples

E-Commerce Events

Tinybird.trackEvent('add_to_cart', {
  partnumber: 'A1708 (EMC 3164)',
  quantity: 1,
  price: 299.99,
  currency: 'USD'
})

User Interactions

document.querySelector('#cta-button').addEventListener('click', () => {
  Tinybird.trackEvent('cta_click', {
    button_id: 'cta-button',
    button_text: 'Get Started',
    page: window.location.pathname
  })
})

Feature Usage

Tinybird.trackEvent('search', {
  query: searchInput.value,
  results_count: results.length,
  filters_applied: activeFilters.length
})

Event Data Structure

Custom events are sent with the same structure as page views:
{
  "timestamp": "2024-03-11T14:23:45.123Z",
  "action": "add_to_cart",
  "version": "1",
  "session_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "tenant_id": "my-tenant",
  "domain": "example.com",
  "payload": {
    "partnumber": "A1708 (EMC 3164)",
    "quantity": 1,
    "price": 299.99,
    "currency": "USD"
  }
}
action
string
The event name you specified in trackEvent().
payload
object
The custom properties you provided, plus any global attributes.

Implementation Details

Event Sending Function

From the source code (middleware/src/index.js:224-283):
async function _sendEvent(name, payload) {
  _setSessionId()

  if (!_isValidUserAgent(window.navigator.userAgent)) {
    return
  }

  let url
  if (proxyUrl) {
    url = proxyUrl
  } else if (proxy) {
    url = `${proxy}/api/tracking`
  } else if (host) {
    host = host.replaceAll(/\/+$/gm, '')
    url = `${host}/v0/events?name=${DATASOURCE}&token=${token}`
  } else {
    url = `https://api.tinybird.co/v0/events?name=${DATASOURCE}&token=${token}`
  }

  let processedPayload
  if (stringifyPayload) {
    processedPayload = _maskSuspiciousAttributes(payload)
    processedPayload = Object.assign({}, JSON.parse(processedPayload), globalAttributes)
    processedPayload = JSON.stringify(processedPayload)

    if (!_isValidPayload(processedPayload)) {
      return
    }
  } else {
    processedPayload = Object.assign({}, payload, globalAttributes)
    const maskedStr = _maskSuspiciousAttributes(processedPayload)

    if (!_isValidPayload(maskedStr)) {
      return
    }

    processedPayload = JSON.parse(maskedStr)
  }

  const session_id = _getSessionId() || _uuidv4()

  const request = new XMLHttpRequest()
  request.open('POST', url, true)
  request.setRequestHeader('Content-Type', 'application/json')
  request.send(
    JSON.stringify({
      timestamp: new Date().toISOString(),
      action: name,
      version: '1',
      session_id,
      tenant_id: tenantId,
      domain,
      payload: processedPayload,
    })
  )
}

// Exposed to window
window.Tinybird = { trackEvent: _sendEvent }

Global Attributes

You can define attributes that will be included in every event using the data-tb-* prefix on the script tag.

Usage

<script
  src="https://unpkg.com/@tinybirdco/flock.js"
  data-token="YOUR_TRACKER_TOKEN"
  data-tb-app-version="2.1.0"
  data-tb-environment="production"
  data-tb-user-segment="premium"
></script>

Result

Every event will include these attributes in the payload:
{
  "action": "add_to_cart",
  "payload": {
    "partnumber": "A1708 (EMC 3164)",
    "quantity": 1,
    "app_version": "2.1.0",
    "environment": "production",
    "user_segment": "premium"
  }
}
Hyphens in attribute names are automatically converted to underscores. data-tb-my-custom-attr becomes my_custom_attr

Implementation

From the source code (middleware/src/index.js:33-40):
let globalAttributes = {}

for (const attr of document.currentScript.attributes) {
  if (attr.name.startsWith('tb_')) {
    globalAttributes[attr.name.slice(3).replace(/-/g, '_')] = attr.value
  }
  if (attr.name.startsWith('data-tb-')) {
    globalAttributes[attr.name.slice(8).replace(/-/g, '_')] = attr.value
  }
}

Privacy & Security

Automatic PII Masking

The tracker automatically masks potentially sensitive fields:
const attributesToMask = [
  'username', 'user', 'user_id', 'userid',
  'password', 'pass', 'pin', 'passcode',
  'token', 'api_token', 'email', 'address',
  'phone', 'sex', 'gender', 'order', 'order_id',
  'orderid', 'payment', 'credit_card',
]
Before masking:
{
  "email": "[email protected]",
  "order_id": "12345"
}
After masking:
{
  "email": "********",
  "order_id": "********"
}
While automatic masking helps, never intentionally track PII like passwords, credit card numbers, or personal identification numbers.

Payload Validation

Events are validated before sending:
function _isValidPayload(payloadStr) {
  if (!payloadStr || typeof payloadStr !== 'string') {
    return false
  }
  if (payloadStr.length < 2 || payloadStr.length > 10240) {
    return false
  }
  return true
}
  • Minimum: 2 bytes
  • Maximum: 10,240 bytes (10 KB)

Best Practices

Use snake_case for event names and property keys:Good:
Tinybird.trackEvent('add_to_cart', {
  product_id: '123',
  product_name: 'Analytics Tool'
})
Bad:
Tinybird.trackEvent('AddToCart', {
  ProductID: '123',
  'Product Name': 'Analytics Tool'
})
Only include necessary data. Large payloads may be rejected.Good:
Tinybird.trackEvent('search', {
  query: 'analytics',
  results: 42
})
Bad:
Tinybird.trackEvent('search', {
  query: 'analytics',
  results: results.map(r => ({ /* full object */ }))
})
Event names should describe what happened, not implementation details.Good:
  • purchase_complete
  • video_play
  • signup_start
Bad:
  • button_click
  • div_123_interaction
  • function_called
Add contextual information to help with analysis.
Tinybird.trackEvent('error_occurred', {
  error_code: 'AUTH_001',
  error_message: 'Invalid token',
  page: window.location.pathname,
  user_action: 'login_attempt'
})

Testing Events

To test your custom events during development:
  1. Open browser console and verify the Tinybird object exists:
    console.log(window.Tinybird)
    // { trackEvent: ƒ _sendEvent(name, payload) }
    
  2. Manually trigger events:
    Tinybird.trackEvent('test_event', { test: true })
    
  3. Check network tab for POST requests to Tinybird
  4. Verify in Tinybird UI that events appear in your data source

Framework Integration

import { useEffect } from 'react'

function ProductCard({ product }) {
  const handleAddToCart = () => {
    // Track event
    window.Tinybird.trackEvent('add_to_cart', {
      product_id: product.id,
      product_name: product.name,
      price: product.price
    })
    
    // Add to cart logic
    addToCart(product)
  }
  
  return (
    <button onClick={handleAddToCart}>
      Add to Cart
    </button>
  )
}

Next Steps

Web Vitals

Track Core Web Vitals performance metrics

Configuration

Advanced configuration options

Build docs developers (and LLMs) love