Skip to main content
Custom events are lightweight annotations you emit from your code. They are stored as span events (the events table) and show up in the span Events view. They do not create Signals or appear in the Signals list. Unlike spans (which represent units of work), custom events are useful for:
  • Tracking outcomes and key checkpoints
  • Adding structured metadata for later SQL queries
  • Creating high-signal analytics without parsing span inputs/outputs

Emitting custom events

Emit events from inside a span context (for example, inside observe() / an @observed function) so the event attaches to the correct trace. If you call Laminar.event outside of a span, Laminar creates a span and attaches the event to it.
import { Laminar, observe } from '@lmnr-ai/lmnr';

await observe({ name: 'extractProduct' }, async () => {
  // ... your logic ...

  Laminar.event({
    name: 'extraction.completed',
    attributes: {
      url: 'https://example.com/product/123',
      fields_extracted: 12,
      duration_ms: 847,
    },
  });
});
See also: Laminar.event(options)

Best practices

  • Keep event name values stable and low-cardinality (don’t include request IDs).
  • Put details into attributes (and avoid sensitive data).
  • Emit events inside a span context so they attach to the correct trace.

Viewing custom events

In the Laminar UI:
  • Open a trace, select a span, and view its Events tab.
  • Query custom events in the SQL Editor using the events table.
If you want LLM-extracted signals, see LLM Signals.