Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.vibefollow.com/llms.txt

Use this file to discover all available pages before exploring further.

The vf.events resource handles custom event tracking and high-volume batching. Use it for anything outside the canonical 9 lifecycle events.

track(eventName, userId, properties?)

Emit a single event. Idempotent on the server when the auto-generated Idempotency-Key is preserved across retries (handled by the client).
await vf.events.track('dashboard_created', 'usr_42', {
  source: 'wizard',
  workspaceId: 'ws_3',
});
eventName
string
required
Event name. Snake_case recommended. The backend validates only that it’s a non-empty string — reserved-name collisions return ValidationError.
userId
string
required
External user ID.
properties
EventProperties
JSON-serialisable property bag. Defaults to {}.
Returns: Promise<void>.

batch(options?)

Returns an EventBatch — an in-memory buffer that flushes in one POST. Useful for backfills and high-volume write paths.
const batch = vf.events.batch({
  maxSize: 100,
  maxAgeMs: 5_000,
});

for (const row of historicalEvents) {
  batch.track(row.name, row.userId, row.properties);
}

await batch.flush();
options.maxSize
number
default:"100"
Auto-flush when the queue reaches this size.
options.maxAgeMs
number
default:"5000"
Auto-flush when this many milliseconds have elapsed since the first queued event.

EventBatch

batch.track(eventName, userId, properties?)

Enqueue an event. Same shape as events.track(), but synchronous — returns void. Auto-flushes when the buffer fills or the age timer fires.
batch.track('dashboard_created', 'usr_42', { source: 'wizard' });

batch.flush()

Drain the queue and POST to /api/v1/events/batch. No-op when empty. Always await — the returned promise resolves only after the network round-trip.
await batch.flush();
Always await batch.flush() before process exit. Lambda-style runtimes that freeze the event loop will lose any queue still in memory. Long-running servers should also call flush() in a shutdown hook (SIGTERM handler).

Trade-offs

Use caseChoose
Real-time, interactiveevents.track() — per-event POST
Backfilling historyevents.batch() — amortised HTTP
Stream from data warehouseevents.batch()
Webhook fan-out / fan-inevents.track() — small enough that batching adds latency
Single-event POST is cheap (sub-100ms typical). Don’t reach for the batch API unless you’re moving thousands of events.

Failure modes

Auto-retried up to maxRetries. Throws on exhaustion. The SDK preserves the Idempotency-Key across retries so duplicates are deduped server-side within 24 hours.
Throws — the queue is already drained at this point. Re-enqueueing on failure would risk duplicates.
Synchronously triggers flush(); the next track() after that just enqueues normally.
In-memory queue is lost. Always await flush() in a shutdown handler.