Skip to main content

Hooks

Hooks are the middleware layer for the event pipeline. They execute before and after provider dispatch, enabling cross-cutting concerns like schema validation, sampling, consent checking, PII stripping, and logging — without modifying provider code.

Lifecycle

Before runs once and gates all providers. After, Error, and Finally fire once per provider result. The registration order is api → client → provider for Before and reversed for After/Error/Finally.

The Hook interface

type Hook interface {
Before(ctx context.Context, hc HookContext, hints HookHints) (*EventEnvelope, error)
After(ctx context.Context, hc HookContext, result HookResult, hints HookHints) error
Error(ctx context.Context, hc HookContext, err error, hints HookHints)
Finally(ctx context.Context, hc HookContext, result HookResult, hints HookHints)
}

Use hooks.UnimplementedHook as a base and override only the stages you need:

type MyHook struct {
hooks.UnimplementedHook
}

func (h *MyHook) Before(ctx context.Context, hc hooks.HookContext, hints hooks.HookHints) (*hooks.EventEnvelope, error) {
// Inspect or mutate the event before dispatch
if hc.EventName == "Product Viewed" {
hc.Envelope.Properties["hook_applied"] = true
}
return hc.Envelope, nil
}

HookContext

HookContext is passed to every hook stage:

FieldTypeDescription
Operationstring"track", "identify", etc.
EventNamestringThe event name
ContextAnalyticsContextMerged context chain at time of call
Envelope*EventEnvelopeMutable event (Before only)
ProviderProviderMetadataWhich provider this result is for (After/Error/Finally)

EventEnvelope

EventEnvelope is the mutable event representation passed through Before hooks. Modifying it affects what all providers receive:

type EventEnvelope struct {
Name string
Properties map[string]any
// ... identity fields
}

Returning an error from Before cancels dispatch entirely and marks the event as Dropped.

Registering hooks

Hooks can be registered at three scopes:

// API-level (applies to all clients)
analytics.AddGlobalHook(myHook)

// Client-level
client := analytics.NewClient(
analytics.WithHooks(validation.New(lookup), sampling.New(samplingHook)),
)

// Provider-level (from Provider.Hooks())

Built-in hooks

HookPackageStatus
Schema validationhooks/validation✅ Available
Samplinghooks/sampling✅ Available
Structured logginghooks/logging❌ Planned
OpenTelemetry spanshooks/otel❌ Planned

Writing a custom hook

See Hooks — Custom.