Headlessly
Analytics

Event

Immutable tracked actions -- pageviews, API calls, sign-ups, webhooks, and every interaction across the system.

Schema

import { Noun } from 'digital-objects'

export const Event = Noun('Event', {
  name: 'string!',
  type: 'string!',
  data: 'json',
  source: 'Browser | Node | API | Snippet',
  sessionId: 'string',
  userId: 'string',
  anonymousId: 'string',
  organization: '-> Organization',
  timestamp: 'datetime!',
  url: 'string',
  path: 'string',
  referrer: 'string',
  properties: 'json',
  update: null,
  delete: null,
})

Fields

FieldTypeRequiredDescription
namestringYesEvent name (e.g. page_view, signup, deal_won)
typestringYesEvent classification (e.g. track, identify, page)
datajsonNoArbitrary event payload
sourceenumNoOrigin: Browser, Node, API, or Snippet
sessionIdstringNoBrowser or agent session identifier
userIdstringNoAuthenticated user ID
anonymousIdstringNoAnonymous tracking ID (pre-auth)
organization-> OrganizationNoTenant the event belongs to
timestampdatetimeYesWhen the event occurred
urlstringNoFull page URL (for browser events)
pathstringNoURL path component
referrerstringNoReferring URL
propertiesjsonNoStructured event properties

Relationships

FieldDirectionTargetDescription
organization->OrganizationTenant this event belongs to

Verbs

VerbEventDescription
createCreatedAppend a new event to the immutable log

Event is immutable -- update: null and delete: null opt out of those CRUD verbs. Once created, an event can never be modified or removed. This is the foundation of trust: every state is reconstructable via time travel.

Verb Lifecycle

import { Event } from '@headlessly/analytics'

// BEFORE hook -- validate or enrich before persistence
Event.creating(event => {
  if (!event.timestamp) {
    event.timestamp = new Date().toISOString()
  }
})

// Execute
await Event.create({
  name: 'page_view',
  type: 'track',
  source: 'Browser',
  url: 'https://acme.dev/pricing',
  path: '/pricing',
  timestamp: new Date().toISOString(),
})

// AFTER hook -- forward to external services
Event.created(event => {
  console.log(`[${event.source}] ${event.name} at ${event.timestamp}`)
})

Immutability

Event has no status state machine. Events are append-only records in an immutable log. This design enables:

  • Time travel: Reconstruct any state at any point in time by replaying events
  • Audit trails: Complete, tamper-proof history of every action
  • Event sourcing: Downstream systems (Metric, Funnel, Goal) derive state from the event stream
  • External forwarding: Events are forwarded to GA, Sentry, PostHog, and stored in the Iceberg R2 lakehouse

Cross-Domain Patterns

Event is the raw signal that feeds every other analytics entity. Every verb on every entity in the system emits an event:

import { Event } from '@headlessly/analytics'
import { Contact } from '@headlessly/crm'

// React to CRM events
Contact.qualified((contact, $) => {
  $.Event.create({
    name: 'contact_qualified',
    type: 'track',
    source: 'API',
    userId: contact.$createdBy,
    properties: JSON.stringify({
      contactId: contact.$id,
      stage: contact.stage,
    }),
    timestamp: new Date().toISOString(),
  })
})
  • CRM: Contact, Lead, and Deal lifecycle events tracked as analytics events
  • Billing: Stripe webhooks (subscription created, invoice paid, payment failed) captured as events
  • Marketing: Campaign interactions (email opened, link clicked, form submitted) recorded as events
  • Support: Ticket creation, resolution, and satisfaction responses logged as events
  • Funnels: Funnel step completion is determined by matching event names

Query Examples

SDK

import { Event } from '@headlessly/analytics'

// Find recent page views
const views = await Event.find({
  name: 'page_view',
  source: 'Browser',
})

// Get a specific event
const event = await Event.get('event_fX9bL5nRd')

// Find all events in a session
const session = await Event.find({
  sessionId: 'sess_kT7rQmNx',
})

MCP

headless.ly/mcp#search
{
  "type": "Event",
  "filter": { "name": "page_view", "source": "Browser" },
  "sort": { "timestamp": "desc" },
  "limit": 50
}
headless.ly/mcp#fetch
{ "type": "Event", "id": "event_fX9bL5nRd" }
headless.ly/mcp#do
const events = await $.Event.find({ name: 'signup', source: 'Browser' })

REST

# List events by name
curl https://analytics.headless.ly/~acme/events?name=page_view&source=Browser

# Get a specific event
curl https://analytics.headless.ly/~acme/events/event_fX9bL5nRd

# Create an event
curl -X POST https://analytics.headless.ly/~acme/events \
  -H 'Content-Type: application/json' \
  -d '{"name": "page_view", "type": "track", "source": "Browser", "timestamp": "2025-01-15T10:30:00Z"}'

On this page