NewYour coding agent can read the release notes before it upgrades.Set up the MCP server →
npm · #4167 most downloaded on npm
PostHog Node.js integration
Last release 4 days ago
30 Sep 2026
Ships on a steady schedule
a new release about every 9 days
Most releases are documented
notes for 44 of the last 60 stable releases
1 version withdrawn
withdrawn after publishing
7 years old
335 releases · first in 2020
One column per quarter.
5.55.0 Minor Changes #5140 2d2560a Thanks @dustinbyrne ! - Allow featureFlagsPollingInterval: null to disable automatic local flag polling while retai
2d2560a Thanks @dustinbyrne! - Allow featureFlagsPollingInterval: null to disable automatic local flag polling while retaining initialization and manual refresh.5.54.1 Patch Changes #4832 ac479db Thanks @dustinbyrne ! - Support snake_case feature flag cache payloads while preserving compatibility with camelCas
ac479db Thanks @dustinbyrne! - Support snake_case feature flag cache payloads while preserving compatibility with camelCase providers and cached data.5.54.0 Minor Changes #5099 e3955f8 Thanks @marandaneto ! - Expose feature flag evaluation reasons and preserve them in OpenFeature resolution metadata
e3955f8 Thanks @marandaneto! - Expose feature flag evaluation reasons and preserve them in OpenFeature resolution metadata.5.53.0 Minor Changes #5050 31dd1ad Thanks @posthog ! - Read a feature flag's evaluation runtime with getFeatureFlagEvaluationRuntime(key) and getFeatu
Updated dependencies [ f4704ac ]:
f4704ac Thanks @rubychilds! - Honor filters.holdout during local feature flag evaluation. A user in an experiment holdout now receives the holdout-<id> variant instead of being bucketed into a regular variant, matching how the server evaluates the same flag. The holdout is resolved before the release conditions, so a held-out user never reaches the flag's targeting — including when those conditions would have excluded them, so isFeatureEnabled can return true where it previously returned false. Experiments with an active holdout will see variant assignment change for the held-out share of traffic on upgrade, bringing locally evaluated assignments in line with server-evaluated ones.f4704ac]:
Updated dependencies [ 9cd8ebd , aad7464 ]:
#5018 9cd8ebd Thanks @turnipdabeets! - Stop dropping long spans that end: maxSpanAgeMs now evicts spans only once maxLiveSpans is reached, so a span that runs past the age limit and then ends is exported, and its children are no longer orphaned.
(2026-09-21)
#4800 aad7464 Thanks @marandaneto! - Respect the definitions response's property_matching_version during local feature flag evaluation. Version 2 uses explicit boolean/string equality and per-member array matching, while missing or other versions retain service legacy matching (including empty-array truthiness). Preserve the version in Node definition caches and Convex persisted definitions, and propagate it through person, group, cohort and dependency evaluation without mixing snapshots during reloads. Existing numeric ambiguity fallback and SemVer parsing policies are unchanged.
(2026-09-21)
Updated dependencies [9cd8ebd, aad7464]:
Updated dependencies [ 39a8980 ]:
39a8980 Thanks @decknamec! - Server-side feature flags now resolve in posthog-node and posthog-edge even when a proxy rewrites the request's User-Agent. Flags restricted to the client runtime now resolve to undefined in these SDKs, where a rewritten User-Agent previously let them through.39a8980]:
Updated dependencies [ 07c1045 ]:
07c1045 Thanks @marandaneto! - Capture causes and AggregateError members with relationship metadata and individual stacks, limiting output to 50 entries and 1,000 member inspections.07c1045]:
5.52.2 Patch Changes #4951 55c5142 Thanks @marandaneto ! - Fix Express exception events reporting the initial response status instead of the final HTT
55c5142 Thanks @marandaneto! - Fix Express exception events reporting the initial response status instead of the final HTTP status.Updated dependencies [ 4a3c74b ]:
4a3c74b Thanks @turnipdabeets! - Child spans now share their parent's clock, so a child no longer appears to start before or end after its parent by up to a millisecond, or by more when the system clock is adjusted mid-trace.4a3c74b]:
Updated dependencies [ 19e78cc , 19e78cc , 19e78cc , 19e78cc , 19e78cc , 19e78cc , 19e78cc ]:
19e78cc Thanks @turnipdabeets! - Add experimental distributed tracing to posthog-node: startSpan, withSpan and getActiveSpan record spans against a new traces client option. A service with tracing off still forwards an inbound traceparent, including from spans nested inside the one that received it, so a distributed trace is not severed. A traceparent may be passed as the one-element array req.headersDistinct gives. A beforeSpanSend hook sees every span before it is exported and may edit or drop it, and maxAttributesPerSpan, maxEventsPerSpan, maxAttributeValueLength, maxLiveSpans and maxSpanAgeMs bound what a single span and a single process may hold.#4579 19e78cc Thanks @turnipdabeets! - Stop a throwing getter in metrics.resourceAttributes from breaking every metrics export — the key is recorded as [Unserializable] instead.
(2026-09-10)
#4579 19e78cc Thanks @turnipdabeets! - Honor Retry-After when the ingestion endpoint refuses a logs or metrics batch, instead of retrying on the SDK's own schedule alone. A refusal naming a longer wait extends the one being served, up to five minutes from when it started. Retry delays now carry jitter so clients refused together do not return together, and metrics backs off exponentially across consecutive failures rather than retrying on a fixed interval.
(2026-09-10)
#4579 19e78cc Thanks @turnipdabeets! - Logs and metrics now always send service.name and telemetry.sdk.*, even when a resourceAttributes value is too large to encode in full. Previously that value could crowd them out, and the records reached PostHog with no service attribution.
(2026-09-10)
#4579 19e78cc Thanks @turnipdabeets! - Stop sending logs and metrics batches over 10 MiB, or too large to serialize at all, instead of spending a request to discover the endpoint refuses them.
(2026-09-10)
Updated dependencies [19e78cc, 19e78cc, 19e78cc, 19e78cc, 19e78cc, 19e78cc, 19e78cc]:
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Updated dependencies [ 930de19 , d4eee8f ]:
#4639 1faeb74 Thanks @marandaneto! - Handle empty feature flag evaluation key scopes without running local or remote evaluation.
(2026-08-25)
#4610 930de19 Thanks @marandaneto! - Share feature flag matching, hashing, variant, and payload evaluation helpers across the Node.js and Convex SDKs without changing their runtime-specific SemVer behavior.
(2026-08-25)
Updated dependencies [930de19, d4eee8f]:
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Updated dependencies [`0a0206f`]:
0a0206f Thanks @marandaneto! - Normalize capture timestamp overrides to equivalent UTC ISO strings in the browser and Node.js SDKs and shared core.
(2026-08-14)0a0206f]:
Updated dependencies [`c9086de`]:
c9086de Thanks @carlos-marchal-ph! - Public beta captureAi() / captureAiImmediate(): AI events on a dedicated isolated endpoint with the event UUID returned. New enableFullAiCapture option replaces the internal _useAiLane / _enableMultimodalCapture; wrappers route through the AI endpoint and skip redaction/truncation when set (privacy mode still wins).
(2026-08-13)c9086de]:
Updated dependencies [`a77115b`]:
a77115b Thanks @marandaneto! - Log shutdown timeouts without rejecting, and correct the Node.js shutdown() return type to Promise<void>.
(2026-08-12)a77115b]:
Updated dependencies [`64ba193`]:
#4346 4751b33 Thanks @marandaneto! - Enforce configured request timeouts while consuming Capture V1 response bodies.
(2026-08-06)
#4423 64ba193 Thanks @marandaneto! - Use Node's zlib gzip implementation for Node SDK payloads to avoid sustained memory growth from frequent Web Streams compression.
(2026-08-06)
Updated dependencies [64ba193]:
## 5.48.0 ### Minor Changes - #4342 `fa3457f` Thanks @haacked! - Support the starts_with, not_starts_with, ends_with, and not_ends_with property filte
fa3457f Thanks @haacked! - Support the starts_with, not_starts_with, ends_with, and not_ends_with property filter operators in feature flag local evaluation. Matching is case-insensitive and mirrors icontains, so flags using these operators no longer fall back to remote evaluation.
(2026-08-05)Updated dependencies [`3c40b6c`]:
3c40b6c Thanks @marandaneto! - Keep request timeouts active through response body consumption and clarify eventual event UUID deduplication semantics.
(2026-08-05)3c40b6c]:
Updated dependencies [`d3c4538`]:
d3c4538 Thanks @posthog! - Keep $referring_domain and canonical utm_*/campaign parameters on minimal $feature_flag_called events. Previously the minimal allowlist stripped every campaign parameter, so a flag-called event landing first in a session could set the session's UTM attribution and channel type to NULL in web analytics.
(2026-08-04)d3c4538]:
## 5.47.9 ### Patch Changes - #4350 `facb4c1` Thanks @marandaneto! - Fix enableExceptionAutocapture suppressing Node's default crash on unhandled prom
facb4c1 Thanks @marandaneto! - Fix enableExceptionAutocapture suppressing Node's default crash on unhandled promise rejections; fatal rejections in strict or warn-with-error-code mode and rejections handled by another unhandledRejection listener are no longer captured.
(2026-08-04)Updated dependencies [`821a2c6`]:
d4ffb83 Thanks @marandaneto! - Schedule local feature flag polls after each request settles.
(2026-08-04)821a2c6]:
## 5.47.7 ### Patch Changes - #4352 `1cf9c6d` Thanks @marandaneto! - Bound stack source context reads and skip non-regular or oversized files. (2026-0
1cf9c6d Thanks @marandaneto! - Bound stack source context reads and skip non-regular or oversized files.
(2026-08-03)Updated dependencies [`f3a71a1`]:
f3a71a1 Thanks @marandaneto! - Clear completed lifecycle timeout handles so successful shutdowns do not leave timers running.
(2026-08-03)f3a71a1]:
Updated dependencies [`eb0a793`]:
eb0a793 Thanks @ATKasem! - fix: personProperties and groupProperties on the feature flag methods are no longer typed as Record<string, string>, so numeric and boolean values type-check without a cast. Local evaluation already handled them — matchProperty takes Record<string, any> and compares numerically for gt/gte/lt/lte — only the public types disagreed. These now use the shared Properties type (personProperties?: Properties, groupProperties?: Record<string, Properties>), matching setPersonPropertiesForFlags/setGroupPropertiesForFlags so the any can be narrowed later. Types only, no runtime change.
(2026-08-03)eb0a793]:
Updated dependencies [`7c3a9af`]:
7c3a9af Thanks @marandaneto! - Preserve events added to a full queue while an earlier batch is being flushed.
(2026-08-03)7c3a9af]:
## 5.47.3 ### Patch Changes - #4349 `0e2b371` Thanks @marandaneto! - Apply the feature flag definition request timeout while consuming the response bo
#4349 0e2b371 Thanks @marandaneto! - Apply the feature flag definition request timeout while consuming the response body.
(2026-07-31)
#4349 0e2b371 Thanks @marandaneto! - Keep the feature flag definition request timeout active until the request settles.
(2026-07-31)
Updated dependencies [`b9a241e`]:
b9a241e Thanks @ioannisj! - Fix identify() leaving a user anonymous when the supplied ID already matches the persisted distinct ID (for example after a non-identified bootstrap seeded the same ID). The user is now marked identified and a person-processed $set event is captured. Ports the same fix from posthog-js (browser) to the shared core used by React Native, Node, and posthog-js-lite.
(2026-07-31)b9a241e]:
## 5.47.1 ### Patch Changes - #4351 `15aad20` Thanks @marandaneto! - Isolate NestJS request context per Observable subscription. (2026-07-31)
15aad20 Thanks @marandaneto! - Isolate NestJS request context per Observable subscription.
(2026-07-31)Updated dependencies [`6e7f3ae`]:
6e7f3ae Thanks @ablaszkiewicz! - Emit the release id that posthog-cli injects into your bundle as $release_id on $exception events, so PostHog can attach exceptions to a release without joining through symbol sets. Adds getInjectedReleaseId() to @posthog/core. The property is only attached when an injected release id can be read.
(2026-07-30)6e7f3ae]:
Updated dependencies [`6c8fde0`]:
6c8fde0 Thanks @hpouillot! - Normalize V8 Promise combinator stack frames so input indexes are not treated as source filenames.
(2026-07-23)6c8fde0]:
When the v2 /flags response carries minimalFlagCalledEvents: true (or, for posthog-node local evaluation, the flag-definitions payload carries minimal
#4172 9621830 Thanks @haacked! - send minimal $feature_flag_called events when the server enables it
When the v2 /flags response carries minimalFlagCalledEvents: true (or, for posthog-node local evaluation, the flag-definitions payload carries minimal_flag_called_events: true) and the evaluated flag is not linked to an experiment ($feature_flag_has_experiment === false), $feature_flag_called events are rebuilt from a strict allowlist of flag-evaluation, processing-control, and SDK-identity properties. Super properties, $set/$set_once, the $feature/<key> enumeration, $active_feature_flags, and the context envelope are stripped. Any missing signal (no gate on the response, bootstrapped or locally injected flags, has_experiment unknown) falls back to the full event, and experiment-linked flags always send the full envelope. The gate is stored alongside the cached flags (posthog-js persistence, posthog-node poller state) and is server-controlled, with no SDK-side configuration. before_send runs after the filter and may re-add stripped properties. (2026-07-20)
9621830]:
Updated dependencies [`06d19a2`]:
06d19a2 Thanks @dustinbyrne! - Avoid Next.js Edge runtime warnings for native compression and fatal error handling.
(2026-07-16)06d19a2]:
## 5.45.1 ### Patch Changes - #4164 `9eb2b28` Thanks @dustinbyrne! - Fix the default Node.js event flush interval at 5 seconds. (2026-07-16)
9eb2b28 Thanks @dustinbyrne! - Fix the default Node.js event flush interval at 5 seconds.
(2026-07-16)$feature_flag_called events now carry a $feature_flag_has_experiment boolean sourced from the server's has_experiment flag metadata (the /flags?v=2 re
#4159 fad6d9a Thanks @haacked! - add $feature_flag_has_experiment to $feature_flag_called events
$feature_flag_called events now carry a $feature_flag_has_experiment boolean sourced from the server's has_experiment flag metadata (the /flags?v=2 response for remote evaluation, the /api/feature_flag/local_evaluation definitions for posthog-node local evaluation). The property is only sent when the server explicitly reports has_experiment; it is omitted entirely when the value is unknown (older servers, missing metadata, bootstrapped or locally injected flags). (2026-07-16)
fad6d9a]:
Updated dependencies [`fc2cb2e`]:
fc2cb2e Thanks @eli-r-ph! - Raise the default maxQueueSize from 1000 to 10000. Backend workloads are more likely to burst-enqueue events synchronously ahead of a flush than browser/mobile clients, so the previous default risked silently dropping events under bursty load. An explicit maxQueueSize option still overrides this default.
(2026-07-15)fc2cb2e]:
Backend services can now record metrics through the same statsd-style pre-aggregating client the browser SDK ships, with no OpenTelemetry setup:
#4117 1eddff7 Thanks @DanielVisca! - add the posthog.metrics API (count, gauge, histogram) to posthog-node — alpha
Backend services can now record metrics through the same statsd-style pre-aggregating client the browser SDK ships, with no OpenTelemetry setup:
const client = new PostHog('phc_...', { metrics: { serviceName: 'billing-worker' } })
client.metrics.count('invoices.processed', 1, { attributes: { plan: 'pro' } })
client.metrics.gauge('queue.depth', 42)
client.metrics.histogram('job.duration', 187, { unit: 'ms' })
Samples aggregate in memory and flush as OTLP/JSON to /i/v1/metrics (one data point per series per window). Pending metrics are flushed on shutdown(). Core gains _sendMetricsBatch on PostHogCoreStateless (same outcome contract as _sendLogsBatch) and a shared resolveMetricsConfig, so any core-based SDK can host PostHogMetrics. (2026-07-15)
1eddff7]:
Updated dependencies [`dc2aa5b`]:
dc2aa5b Thanks @posthog! - Expose the error tracking rate-limiter config via the new exceptionRateLimiterRefillRate and exceptionRateLimiterBucketSize options. Burst protection is scoped per exception type (each distinct $exception type gets its own token bucket, with no aggregate cap across types), so these let customers with high-cardinality exception types tune the per-type allowance.
(2026-07-14)dc2aa5b]:
Capture V1 uses Bearer auth, lifts legacy $-sentinel properties into a typed options object, and does per-event partial retry with exponential backoff
#4105 203284a Thanks @eli-r-ph! - Add opt-in Capture V1 support. Set the POSTHOG_CAPTURE_MODE=v1 environment variable to submit analytics events to the Capture V1 endpoint (/i/v1/analytics/events) instead of the legacy /batch/ endpoint, on both the batched and immediate send paths. The default remains v0, so existing behavior is unchanged unless you opt in. Opt-in is env-var-only during the transition (no public option), so nothing on the API surface has to be removed when v1 later becomes the default.
Capture V1 uses Bearer auth, lifts legacy $-sentinel properties into a typed options object, and does per-event partial retry with exponential backoff clamped against Retry-After. Dropped and undelivered events are surfaced on the client error channel as a CaptureV1Error. $ai_* events continue to use the legacy submitter for now, regardless of the capture mode.
In v1 mode, $ai_* events are routed to an isolated in-memory queue and flushed independently of the Capture V1 queue, so the two transports never share a batch and a failure on one cannot re-send events already accepted on the other. Each queue keeps its own retry/durability semantics: the legacy queue re-queues on network failure (retrying on later flushes), while the V1 queue exhausts the sender's own attempt budget per cycle and then surfaces the failure rather than re-queuing. (2026-07-11)
203284a]:
## 5.40.0 ### Minor Changes - #4060 `0b49a4c` Thanks @turnipdabeets! - Add secretKey config as the canonical alias for the deprecated personalApiKey (
0b49a4c Thanks @turnipdabeets! - Add secretKey config as the canonical alias for the deprecated personalApiKey (accepts a Personal API Key or Project Secret API Key).
(2026-07-07)Updated dependencies [`5e7e132`]:
0c11747 Thanks @marandaneto! - Stop duplicating distinct_id inside /flags person properties.
(2026-07-02)5e7e132]:
Updated dependencies [`64e04ba`]:
64e04ba Thanks @marandaneto! - Retry /flags requests that receive HTTP 502 or 504 responses across SDKs that use the shared core flags client.
(2026-07-02)64e04ba]:
Updated dependencies [`a664b81`]:
a664b81 Thanks @marandaneto! - Make Node flush() wait for pending asynchronous SDK work before draining the event queue, so events produced by helpers like captureException() are not missed. Pending work rejections no longer prevent queued events from flushing.
(2026-07-01)a664b81]:
Updated dependencies [`ab118d2`]:
#4029 b36b1cc Thanks @marandaneto! - Call before_send for identify, group identify, and alias events.
(2026-06-30)
#4027 ab118d2 Thanks @marandaneto! - Safely serialize event batches with circular property references instead of crashing during flush.
(2026-06-30)
Updated dependencies [ab118d2]:
Updated dependencies [`0063128`]:
0063128 Thanks @github-actions! - Add groupIdentifyImmediate() to await the network request when identifying a group, mirroring captureImmediate/identifyImmediate/aliasImmediate. Useful in edge/serverless environments where the background queue may not flush. The Convex integration now uses it directly instead of routing $groupidentify through captureImmediate.
(2026-06-30)0063128]:
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Your coding agent can read these notes before it upgrades. Set up the MCP server →