Problem
Today analytics.capture() accepts an optional req (whose posthogContext.source is merged into event properties), and analyticsService.captureException(err, ctx) accepts {distinctId, requestId}. Neither has an explicit source parameter.
Consequence:
- Server-side emissions with no req (
scrap_run_completed from worker callback, compute_consumed from historys, subscription_changed from Stripe webhook, cron tasks) end up with source = null in PostHog.
- Validated 2026-05-11: 100% of Vue events have null source (expected), but all 3 Node events from a manual scrap run also had null source —
worker-callback and stripe-webhook emissions can't be attributed.
Spec
Extend capture({distinctId, event, properties, req, source}) and captureException(err, {distinctId, requestId, source, properties}) to accept an explicit source override. Precedence: explicit source > properties.source > req.posthogContext.source > 'system' default.
Acceptance criteria
Follow-up after merge:
Problem
Today
analytics.capture()accepts an optionalreq(whoseposthogContext.sourceis merged into event properties), andanalyticsService.captureException(err, ctx)accepts{distinctId, requestId}. Neither has an explicitsourceparameter.Consequence:
scrap_run_completedfrom worker callback,compute_consumedfrom historys,subscription_changedfrom Stripe webhook, cron tasks) end up withsource = nullin PostHog.worker-callbackandstripe-webhookemissions can't be attributed.Spec
Extend
capture({distinctId, event, properties, req, source})andcaptureException(err, {distinctId, requestId, source, properties})to accept an explicitsourceoverride. Precedence: explicitsource>properties.source>req.posthogContext.source>'system'default.Acceptance criteria
capture()adds optionalsourceparam to JSDoc + destructurecaptureException()acceptsctx.sourceandctx.properties'system'baked into the merged defaults of both helpersFollow-up after merge:
error-level logs to PostHog Error Tracking via custom transport #3651) uses new captureException ctx.properties