vybekiit 0.7.33 → 0.7.35

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (132) hide show
  1. package/dist/bin.js +818 -92
  2. package/dist/global-skills/eas-app-stores/SKILL.md +2 -2
  3. package/dist/global-skills/eas-hosting/SKILL.md +2 -2
  4. package/dist/global-skills/eas-observe/SKILL.md +7 -15
  5. package/dist/global-skills/eas-observe/references/queries.md +51 -10
  6. package/dist/global-skills/eas-observe/references/setup.md +60 -7
  7. package/dist/global-skills/eas-simulator/SKILL.md +2 -2
  8. package/dist/global-skills/eas-simulator/references/run-your-app.md +2 -0
  9. package/dist/global-skills/eas-update/SKILL.md +2 -2
  10. package/dist/global-skills/eas-update-insights/SKILL.md +2 -2
  11. package/dist/global-skills/eas-workflows/SKILL.md +2 -2
  12. package/dist/global-skills/expo-animation/SKILL.md +2 -2
  13. package/dist/global-skills/expo-app-clip/SKILL.md +2 -2
  14. package/dist/global-skills/expo-brownfield/SKILL.md +2 -2
  15. package/dist/global-skills/expo-data-fetching/SKILL.md +2 -2
  16. package/dist/global-skills/expo-design-system/SKILL.md +2 -2
  17. package/dist/global-skills/expo-dev-client/SKILL.md +4 -2
  18. package/dist/global-skills/expo-dom/SKILL.md +2 -2
  19. package/dist/global-skills/expo-examples/SKILL.md +2 -2
  20. package/dist/global-skills/expo-migrate-module/SKILL.md +2 -2
  21. package/dist/global-skills/expo-module/SKILL.md +2 -2
  22. package/dist/global-skills/expo-native-ui/SKILL.md +2 -2
  23. package/dist/global-skills/expo-overview/SKILL.md +9 -4
  24. package/dist/global-skills/expo-overview/references/launch-without-dev-menu.md +24 -0
  25. package/dist/global-skills/expo-project-structure/SKILL.md +2 -2
  26. package/dist/global-skills/expo-router/SKILL.md +2 -2
  27. package/dist/global-skills/expo-ui/SKILL.md +2 -2
  28. package/dist/global-skills/expo-upgrade/SKILL.md +2 -2
  29. package/dist/global-skills/expo-web-to-native/SKILL.md +2 -2
  30. package/dist/global-skills/expo-web-to-native/references/verify-on-device.md +1 -1
  31. package/dist/global-skills/firebase-app-hosting-basics/SKILL.md +16 -3
  32. package/dist/global-skills/firebase-app-hosting-basics/references/cli_commands.md +10 -12
  33. package/dist/global-skills/firebase-app-hosting-basics/references/configuration.md +2 -1
  34. package/dist/global-skills/firebase-basics/SKILL.md +6 -4
  35. package/dist/global-skills/firebase-basics/references/web_setup.md +3 -3
  36. package/dist/global-skills/instrument-feature-flags/references/adding-feature-flag-code.md +81 -333
  37. package/dist/global-skills/instrument-feature-flags/references/android.md +19 -13
  38. package/dist/global-skills/instrument-feature-flags/references/api.md +17 -13
  39. package/dist/global-skills/instrument-feature-flags/references/best-practices.md +35 -35
  40. package/dist/global-skills/instrument-feature-flags/references/django.md +120 -72
  41. package/dist/global-skills/instrument-feature-flags/references/dotnet.md +48 -102
  42. package/dist/global-skills/instrument-feature-flags/references/elixir.md +4 -10
  43. package/dist/global-skills/instrument-feature-flags/references/flask.md +55 -39
  44. package/dist/global-skills/instrument-feature-flags/references/flutter.md +19 -13
  45. package/dist/global-skills/instrument-feature-flags/references/go.md +21 -13
  46. package/dist/global-skills/instrument-feature-flags/references/ios.md +19 -13
  47. package/dist/global-skills/instrument-feature-flags/references/java.md +4 -12
  48. package/dist/global-skills/instrument-feature-flags/references/laravel.md +18 -16
  49. package/dist/global-skills/instrument-feature-flags/references/next-js.md +44 -54
  50. package/dist/global-skills/instrument-feature-flags/references/nodejs.md +21 -13
  51. package/dist/global-skills/instrument-feature-flags/references/php.md +21 -13
  52. package/dist/global-skills/instrument-feature-flags/references/python.md +22 -14
  53. package/dist/global-skills/instrument-feature-flags/references/react-native.md +18 -13
  54. package/dist/global-skills/instrument-feature-flags/references/react.md +21 -14
  55. package/dist/global-skills/instrument-feature-flags/references/ruby-on-rails.md +31 -68
  56. package/dist/global-skills/instrument-feature-flags/references/ruby.md +21 -13
  57. package/dist/global-skills/instrument-feature-flags/references/rust.md +10 -34
  58. package/dist/global-skills/instrument-feature-flags/references/usage.md +94 -114
  59. package/dist/global-skills/instrument-feature-flags/references/web.md +21 -13
  60. package/dist/global-skills/instrument-product-analytics/references/EXAMPLE-android.md +7 -7
  61. package/dist/global-skills/instrument-product-analytics/references/EXAMPLE-expo.md +2 -2
  62. package/dist/global-skills/instrument-product-analytics/references/EXAMPLE-fastapi.md +1 -1
  63. package/dist/global-skills/instrument-product-analytics/references/EXAMPLE-flask.md +1 -1
  64. package/dist/global-skills/instrument-product-analytics/references/EXAMPLE-laravel.md +2 -2
  65. package/dist/global-skills/instrument-product-analytics/references/EXAMPLE-php.md +1 -1
  66. package/dist/global-skills/instrument-product-analytics/references/EXAMPLE-python.md +1 -1
  67. package/dist/global-skills/instrument-product-analytics/references/EXAMPLE-ruby.md +1 -1
  68. package/dist/global-skills/instrument-product-analytics/references/EXAMPLE-swift.md +4 -4
  69. package/dist/global-skills/instrument-product-analytics/references/android.md +64 -109
  70. package/dist/global-skills/instrument-product-analytics/references/angular.md +32 -28
  71. package/dist/global-skills/instrument-product-analytics/references/astro.md +29 -34
  72. package/dist/global-skills/instrument-product-analytics/references/configuration.md +27 -42
  73. package/dist/global-skills/instrument-product-analytics/references/django.md +120 -72
  74. package/dist/global-skills/instrument-product-analytics/references/dotnet.md +48 -102
  75. package/dist/global-skills/instrument-product-analytics/references/elixir.md +25 -50
  76. package/dist/global-skills/instrument-product-analytics/references/flask.md +55 -39
  77. package/dist/global-skills/instrument-product-analytics/references/flutter.md +94 -96
  78. package/dist/global-skills/instrument-product-analytics/references/go.md +39 -77
  79. package/dist/global-skills/instrument-product-analytics/references/identify-users.md +24 -26
  80. package/dist/global-skills/instrument-product-analytics/references/ios.md +22 -22
  81. package/dist/global-skills/instrument-product-analytics/references/laravel.md +18 -16
  82. package/dist/global-skills/instrument-product-analytics/references/next-js.md +44 -54
  83. package/dist/global-skills/instrument-product-analytics/references/nuxt-js-3-6.md +32 -34
  84. package/dist/global-skills/instrument-product-analytics/references/nuxt-js.md +36 -36
  85. package/dist/global-skills/instrument-product-analytics/references/php.md +42 -87
  86. package/dist/global-skills/instrument-product-analytics/references/posthog-python.md +15 -12
  87. package/dist/global-skills/instrument-product-analytics/references/python.md +106 -154
  88. package/dist/global-skills/instrument-product-analytics/references/react-native.md +97 -176
  89. package/dist/global-skills/instrument-product-analytics/references/react-router-v6.md +29 -40
  90. package/dist/global-skills/instrument-product-analytics/references/react-router-v7-data-mode.md +29 -38
  91. package/dist/global-skills/instrument-product-analytics/references/react-router-v7-declarative-mode.md +29 -40
  92. package/dist/global-skills/instrument-product-analytics/references/react-router-v7-framework-mode.md +32 -37
  93. package/dist/global-skills/instrument-product-analytics/references/ruby-on-rails.md +31 -68
  94. package/dist/global-skills/instrument-product-analytics/references/ruby.md +40 -96
  95. package/dist/global-skills/instrument-product-analytics/references/svelte.md +34 -34
  96. package/dist/global-skills/instrument-product-analytics/references/tanstack-start.md +19 -19
  97. package/dist/global-skills/instrument-product-analytics/references/usage.md +94 -114
  98. package/dist/global-skills/instrument-product-analytics/references/vue-js.md +33 -45
  99. package/dist/global-skills/manifest.json +2 -1
  100. package/dist/global-skills/mongodb-search-and-ai/SKILL.md +40 -22
  101. package/dist/global-skills/mongodb-search-and-ai/references/automated-embedding.md +8 -2
  102. package/dist/global-skills/mongodb-search-and-ai/references/quick-start.md +725 -0
  103. package/dist/global-skills/mongodb-search-and-ai/scripts/quickstart_complete.py +386 -0
  104. package/dist/global-skills/neon/SKILL.md +15 -10
  105. package/dist/global-skills/neon/references/claimable-neon.md +2 -0
  106. package/dist/global-skills/neon-functions/SKILL.md +12 -11
  107. package/dist/global-skills/neon-functions/references/sse.md +2 -2
  108. package/dist/global-skills/neon-object-storage/SKILL.md +5 -4
  109. package/dist/global-skills/neon-postgres/SKILL.md +4 -1
  110. package/dist/global-skills/neon-realtime/SKILL.md +207 -0
  111. package/dist/global-skills/neon-realtime/references/mutations.md +743 -0
  112. package/dist/global-skills/neon-realtime/references/queries.md +509 -0
  113. package/dist/global-skills/neon-realtime/references/reactivity.md +407 -0
  114. package/dist/global-skills/neon-realtime/references/ssr.md +695 -0
  115. package/dist/global-skills/neon-realtime/references/subscribing.md +485 -0
  116. package/dist/global-skills/resend/SKILL.md +4 -2
  117. package/dist/global-skills/resend/references/inboxes.md +377 -0
  118. package/dist/global-skills/resend/references/webhooks.md +9 -0
  119. package/dist/global-skills/stripe-best-practices/SKILL.md +1 -1
  120. package/dist/global-skills/stripe-best-practices/references/connect.md +1 -1
  121. package/dist/global-skills/stripe-best-practices/references/security.md +9 -9
  122. package/dist/global-skills/vybekiitReferences/backend/AGENTS.md +1 -1
  123. package/dist/global-skills/vybekiitReferences/backend/agent/goal-routes.json +303 -18
  124. package/dist/global-skills/vybekiitReferences/extension/AGENTS.md +1 -1
  125. package/dist/global-skills/vybekiitReferences/extension/agent/goal-routes.json +352 -18
  126. package/dist/global-skills/vybekiitReferences/mobile/AGENTS.md +1 -1
  127. package/dist/global-skills/vybekiitReferences/mobile/agent/goal-routes.json +413 -22
  128. package/dist/global-skills/vybekiitReferences/spa/AGENTS.md +1 -1
  129. package/dist/global-skills/vybekiitReferences/spa/agent/goal-routes.json +391 -21
  130. package/dist/global-skills/vybekiitReferences/web/AGENTS.md +14 -2
  131. package/dist/global-skills/vybekiitReferences/web/agent/goal-routes.json +490 -32
  132. package/package.json +8 -8
@@ -2,6 +2,20 @@
2
2
 
3
3
  # Android Feature Flags installation
4
4
 
5
+ Canonical URL: https://posthog.com/docs/feature-flags/installation/android
6
+
7
+ > **Cloud region:** This page uses US Cloud hosts. For an EU Cloud project, use `https://eu.i.posthog.com` instead of `https://us.i.posthog.com` (ingestion host).
8
+
9
+ ## On this page
10
+
11
+ - [Install the dependency](#install-the-dependency)
12
+ - [Configure PostHog](#configure-posthog)
13
+ - [Send events](#send-events)
14
+ - [Evaluate boolean feature flags](#evaluate-boolean-feature-flags)
15
+ - [Evaluate multivariate feature flags](#evaluate-multivariate-feature-flags)
16
+ - [Running experiments](#running-experiments)
17
+ - [Next steps](#next-steps)
18
+
5
19
  1. 1
6
20
 
7
21
  ## Install the dependency
@@ -128,16 +142,8 @@
128
142
 
129
143
  | Resource | Description |
130
144
  | --- | --- |
131
- | [Creating a feature flag](/docs/feature-flags/creating-feature-flags.md) | How to create a feature flag in PostHog |
132
- | [Adding feature flag code](/docs/feature-flags/adding-feature-flag-code.md) | How to check flags in your code for all platforms |
133
- | [Framework-specific guides](/docs/feature-flags/tutorials.md#framework-guides) | Setup guides for React Native, Next.js, Flutter, and other frameworks |
134
- | [How to do a phased rollout](/tutorials/phased-rollout.md) | Gradually roll out features to minimize risk |
135
- | [More tutorials](/docs/feature-flags/tutorials.md) | Other real-world examples and use cases |
136
-
137
- ### Still have questions?
138
-
139
- Ask PostHog AI
140
-
141
- ### Was this page useful?
142
-
143
- HelpfulCould be better
145
+ | [Creating a feature flag](https://posthog.com/docs/feature-flags/creating-feature-flags.md) | How to create a feature flag in PostHog |
146
+ | [Adding feature flag code](https://posthog.com/docs/feature-flags/adding-feature-flag-code.md) | How to check flags in your code for all platforms |
147
+ | [Framework-specific guides](https://posthog.com/docs/feature-flags/tutorials.md#framework-guides) | Setup guides for React Native, Next.js, Flutter, and other frameworks |
148
+ | [How to do a phased rollout](https://posthog.com/tutorials/phased-rollout.md) | Gradually roll out features to minimize risk |
149
+ | [More tutorials](https://posthog.com/docs/feature-flags/tutorials.md) | Other real-world examples and use cases |
@@ -2,6 +2,18 @@
2
2
 
3
3
  # API Feature Flags installation
4
4
 
5
+ Canonical URL: https://posthog.com/docs/feature-flags/installation/api
6
+
7
+ > **Cloud region:** This page uses US Cloud hosts. For an EU Cloud project, use `https://eu.i.posthog.com` instead of `https://us.i.posthog.com` (ingestion host).
8
+
9
+ ## On this page
10
+
11
+ - [Evaluate the feature flag value using flags](#evaluate-the-feature-flag-value-using-flags)
12
+ - [Include feature flag information when capturing events](#include-feature-flag-information-when-capturing-events)
13
+ - [Send a $feature_flag_called event](#send-a-feature_flag_called-event)
14
+ - [Running experiments](#running-experiments)
15
+ - [Next steps](#next-steps)
16
+
5
17
  1. 1
6
18
 
7
19
  ## Evaluate the feature flag value using flags
@@ -178,16 +190,8 @@
178
190
 
179
191
  | Resource | Description |
180
192
  | --- | --- |
181
- | [Creating a feature flag](/docs/feature-flags/creating-feature-flags.md) | How to create a feature flag in PostHog |
182
- | [Adding feature flag code](/docs/feature-flags/adding-feature-flag-code.md) | How to check flags in your code for all platforms |
183
- | [Framework-specific guides](/docs/feature-flags/tutorials.md#framework-guides) | Setup guides for React Native, Next.js, Flutter, and other frameworks |
184
- | [How to do a phased rollout](/tutorials/phased-rollout.md) | Gradually roll out features to minimize risk |
185
- | [More tutorials](/docs/feature-flags/tutorials.md) | Other real-world examples and use cases |
186
-
187
- ### Still have questions?
188
-
189
- Ask PostHog AI
190
-
191
- ### Was this page useful?
192
-
193
- HelpfulCould be better
193
+ | [Creating a feature flag](https://posthog.com/docs/feature-flags/creating-feature-flags.md) | How to create a feature flag in PostHog |
194
+ | [Adding feature flag code](https://posthog.com/docs/feature-flags/adding-feature-flag-code.md) | How to check flags in your code for all platforms |
195
+ | [Framework-specific guides](https://posthog.com/docs/feature-flags/tutorials.md#framework-guides) | Setup guides for React Native, Next.js, Flutter, and other frameworks |
196
+ | [How to do a phased rollout](https://posthog.com/tutorials/phased-rollout.md) | Gradually roll out features to minimize risk |
197
+ | [More tutorials](https://posthog.com/docs/feature-flags/tutorials.md) | Other real-world examples and use cases |
@@ -2,11 +2,23 @@
2
2
 
3
3
  # Best practices for production-ready flags
4
4
 
5
+ Canonical URL: https://posthog.com/docs/feature-flags/best-practices
6
+
7
+ ## On this page
8
+
9
+ - [Checklist](#checklist)
10
+ - [Flags are pure functions](#flags-are-pure-functions)
11
+ - [Unexpected results are almost always input problems](#unexpected-results-are-almost-always-input-problems)
12
+ - [Resolve identity before evaluating flags](#resolve-identity-before-evaluating-flags)
13
+ - [Evaluation architecture](#evaluation-architecture)
14
+ - [Flag hygiene](#flag-hygiene)
15
+ - [Further reading](#further-reading)
16
+
5
17
  ## Checklist
6
18
 
7
19
  - [Call `identify()` before evaluating flags](#resolve-identity-before-evaluating-flags) – the hash uses the wrong ID otherwise. This is the most common input problem.
8
20
  - [Evaluate flags server-side with local evaluation](#server-side-local-evaluation-is-the-recommended-default) – explicit inputs, your data right there, no workarounds.
9
- - [Bootstrap client-side flags](#have-the-value-before-you-need-it) – client-side evaluation is async. [Bootstrap](/docs/feature-flags/bootstrapping.md) to eliminate the gap.
21
+ - [Bootstrap client-side flags](#have-the-value-before-you-need-it) – client-side evaluation is async. [Bootstrap](https://posthog.com/docs/feature-flags/bootstrapping.md) to eliminate the gap.
10
22
  - [Handle `undefined` explicitly](#undefined-is-not-false) – it means "not evaluated yet," not `false`.
11
23
  - [Evaluate once, record the result](#evaluate-once-not-continuously) – a flag is a one-time signal. Re-evaluate only on meaningful state changes.
12
24
  - [Evaluate where the data lives](#evaluate-where-the-data-lives) – if the data is on your server, evaluate there.
@@ -58,13 +70,13 @@ So when a flag returns something you didn't expect, **the flag is fine, the prob
58
70
 
59
71
  If you keep running into flag issues and they're not [incidents](https://status.posthog.com), the conversation isn't about PostHog's flag behavior – it's about how your application coordinates the data that flags depend on. That's an engineering conversation about identity flows, property syncing, and evaluation architecture. No single config tweak fixes it.
60
72
 
61
- We're here to help with that – this guide, [PostHog AI](/docs/feature-flags/manage-flags-ai.md), and [professional services](https://posthog.com/professional-services) all exist for exactly this. But the starting point is always the same: **look at the inputs.**
73
+ We're here to help with that – this guide, [PostHog AI](https://posthog.com/docs/feature-flags/manage-flags-ai.md), and [professional services](https://posthog.com/professional-services) all exist for exactly this. But the starting point is always the same: **look at the inputs.**
62
74
 
63
75
  When something goes wrong, in order of likelihood:
64
76
 
65
- 1. **Input problems** (most common). Wrong distinct ID, missing properties, changed flag definition. PostHog gives you tools to get the coordination right – [bootstrapping](/docs/feature-flags/bootstrapping.md), [property overrides](/docs/feature-flags/property-overrides.md), [server-side evaluation](/docs/feature-flags/local-evaluation.md).
77
+ 1. **Input problems** (most common). Wrong distinct ID, missing properties, changed flag definition. PostHog gives you tools to get the coordination right – [bootstrapping](https://posthog.com/docs/feature-flags/bootstrapping.md), [property overrides](https://posthog.com/docs/feature-flags/property-overrides.md), [server-side evaluation](https://posthog.com/docs/feature-flags/local-evaluation.md).
66
78
  2. **Output problems.** The flag returned the right value but your code misread it – `undefined` treated as `false`, no handling for the loading gap, evaluating repeatedly instead of recording the result.
67
- 3. **Actual incidents.** Check [status.posthog.com](https://status.posthog.com). If nothing there, it's #1 or #2. And even here: with [server-side local evaluation](/docs/feature-flags/local-evaluation.md), the SDK evaluates against cached flag definitions locally. PostHog being unreachable doesn't affect flags that are already cached. Add per-flag safe defaults and even a cold start during an outage returns usable values. An incident only breaks your flags if your implementation depends on PostHog being available at request time – which is itself an implementation gap you can close.
79
+ 3. **Actual incidents.** Check [status.posthog.com](https://status.posthog.com). If nothing there, it's #1 or #2. And even here: with [server-side local evaluation](https://posthog.com/docs/feature-flags/local-evaluation.md), the SDK evaluates against cached flag definitions locally. PostHog being unreachable doesn't affect flags that are already cached. Add per-flag safe defaults and even a cold start during an outage returns usable values. An incident only breaks your flags if your implementation depends on PostHog being available at request time – which is itself an implementation gap you can close.
68
80
 
69
81
  ## Resolve identity before evaluating flags
70
82
 
@@ -72,17 +84,17 @@ Identity is the most common input problem. The hash takes two inputs: the flag k
72
84
 
73
85
  If you call `identify()` after a flag has already been evaluated, the flag likely used the anonymous ID. The hash produced one result. After `identify()`, the distinct ID changes, the hash changes, and the next evaluation returns a different variant. You see a "flip" – but it's because the input changed.
74
86
 
75
- Call [`identify()`](/docs/product-analytics/identify.md) before any flag evaluation in auth flows. If you can't guarantee that timing, [bootstrap](/docs/feature-flags/bootstrapping.md) with the stable ID at init so the distinct ID is correct from the first millisecond. See [keeping flag evaluations stable](/docs/feature-flags/stable-identity-for-flags.md) for the full picture.
87
+ Call [`identify()`](https://posthog.com/docs/product-analytics/identify.md) before any flag evaluation in auth flows. If you can't guarantee that timing, [bootstrap](https://posthog.com/docs/feature-flags/bootstrapping.md) with the stable ID at init so the distinct ID is correct from the first millisecond. See [keeping flag evaluations stable](https://posthog.com/docs/feature-flags/stable-identity-for-flags.md) for the full picture.
76
88
 
77
89
  **SPA-specific timing.** In single-page applications, `identify()` and event captures often fire from different components during the same navigation in unpredictable order. The SDK updates the `distinct_id` synchronously when `identify()` runs, but if `capture()` was called first in the same execution frame, that event uses the anonymous ID. The fix: call `identify()` before the navigation that mounts post-auth components – in Vue, in `beforeEach` before `next()`; in React, before `navigate()`, not in a `useEffect` inside the target route.
78
90
 
79
91
  ### Don't rely on flag persistence to fix identity gaps
80
92
 
81
- If you've enabled [experience continuity](/docs/feature-flags/creating-feature-flags.md#persisting-feature-flags-across-authentication-steps-optional) (flag persistence across authentication), consider what that's telling you: the distinct ID is changing during your session, and you need PostHog to paper over it.
93
+ If you've enabled [experience continuity](https://posthog.com/docs/feature-flags/creating-feature-flags.md#persisting-feature-flags-across-authentication-steps-optional) (flag persistence across authentication), consider what that's telling you: the distinct ID is changing during your session, and you need PostHog to paper over it.
82
94
 
83
- That comes at a cost. Experience continuity couples flag evaluation with database writes – every evaluation reads and writes to the DB to persist the result. This mixes two concerns (evaluation and storage) that should be separate, and it's the source of [known bugs](https://github.com/PostHog/posthog-js/issues/2623) where values can still change after `identify()`. It also means no support for [local evaluation](/docs/feature-flags/local-evaluation.md) and slower flag responses.
95
+ That comes at a cost. Experience continuity couples flag evaluation with database writes – every evaluation reads and writes to the DB to persist the result. This mixes two concerns (evaluation and storage) that should be separate, and it's the source of [known bugs](https://github.com/PostHog/posthog-js/issues/2623) where values can still change after `identify()`. It also means no support for [local evaluation](https://posthog.com/docs/feature-flags/local-evaluation.md) and slower flag responses.
84
96
 
85
- The better fix is to make persistence unnecessary. Use [device bucketing](/docs/feature-flags/device-bucketing.md) for single-device consistency, or design your identity flow so the distinct ID [never changes](/docs/feature-flags/stable-identity-for-flags.md). If you need experience continuity today, treat it as a migration path toward proper [identity resolution](/docs/product-analytics/identity-resolution.md), not a permanent solution. The identity gap it papers over is the root cause of the most common flag issues – closing that gap eliminates the need for persistence entirely.
97
+ The better fix is to make persistence unnecessary. Use [device bucketing](https://posthog.com/docs/feature-flags/device-bucketing.md) for single-device consistency, or design your identity flow so the distinct ID [never changes](https://posthog.com/docs/feature-flags/stable-identity-for-flags.md). If you need experience continuity today, treat it as a migration path toward proper [identity resolution](https://posthog.com/docs/product-analytics/identity-resolution.md), not a permanent solution. The identity gap it papers over is the root cause of the most common flag issues – closing that gap eliminates the need for persistence entirely.
86
98
 
87
99
  ## Evaluation architecture
88
100
 
@@ -103,13 +115,13 @@ If you target a flag on `plan_type: "pro"`, your app originally told PostHog thi
103
115
 
104
116
  If you evaluate client-side instead, the SDK needs to fetch that property from PostHog's servers – a round-trip to look up what you originally sent it. Any flag check before that completes evaluates against incomplete data.
105
117
 
106
- If you must evaluate client-side, use [`setPersonPropertiesForFlags()`](/docs/feature-flags/property-overrides.md#manual-overrides-with-setpersonpropertiesforflags) to set properties locally before evaluation. This avoids the round-trip when you already have the data in the browser.
118
+ If you must evaluate client-side, use [`setPersonPropertiesForFlags()`](https://posthog.com/docs/feature-flags/property-overrides.md#manual-overrides-with-setpersonpropertiesforflags) to set properties locally before evaluation. This avoids the round-trip when you already have the data in the browser.
107
119
 
108
120
  Property targeting is fine – just understand that the further the evaluation is from the data, the more async complexity you take on.
109
121
 
110
122
  ### Server-side local evaluation is the recommended default
111
123
 
112
- [Server-side local evaluation](/docs/feature-flags/local-evaluation.md) is where the pure function model is fully legible:
124
+ [Server-side local evaluation](https://posthog.com/docs/feature-flags/local-evaluation.md) is where the pure function model is fully legible:
113
125
 
114
126
  - **All inputs are explicit.** You pass the distinct ID and properties directly. When something's wrong, you log what you passed.
115
127
  - **Your data is right there.** User plan, account type, permissions – it's in your database at request time. No syncing, no fetching.
@@ -121,7 +133,7 @@ Client-side evaluation is right when you need properties only available in the b
121
133
 
122
134
  Client-side flag evaluation is async – the SDK needs to fetch values from PostHog. Any flag check before that completes returns `undefined`, not `false`.
123
135
 
124
- **[Bootstrap](/docs/feature-flags/bootstrapping.md) is the fix.** Evaluate flags server-side and pass values to the client at init. The value exists before the page renders – no gap, no flicker.
136
+ **[Bootstrap](https://posthog.com/docs/feature-flags/bootstrapping.md) is the fix.** Evaluate flags server-side and pass values to the client at init. The value exists before the page renders – no gap, no flicker.
125
137
 
126
138
  If you can't bootstrap, use `onFeatureFlags()` to wait. This means you will need a loading state (spinner, skeleton) until flags arrive – it prevents showing the wrong variant but doesn't prevent a delay.
127
139
 
@@ -129,8 +141,6 @@ If you can't bootstrap, use `onFeatureFlags()` to wait. This means you will need
129
141
 
130
142
  `posthog.getFeatureFlag()` returns `undefined` before flags load. That means "not evaluated yet," not "flag is off."
131
143
 
132
- JavaScript
133
-
134
144
  ```javascript
135
145
  // Returns undefined before flags load – not false
136
146
  if (posthog.getFeatureFlag('my-experiment') === 'test') {
@@ -138,9 +148,9 @@ if (posthog.getFeatureFlag('my-experiment') === 'test') {
138
148
  }
139
149
  ```
140
150
 
141
- Handle it with [bootstrap](/docs/feature-flags/bootstrapping.md) (preferred) or `onFeatureFlags()` (adds a loading state). You can check the current identity with `posthog.get_distinct_id()`.
151
+ Handle it with [bootstrap](https://posthog.com/docs/feature-flags/bootstrapping.md) (preferred) or `onFeatureFlags()` (adds a loading state). You can check the current identity with `posthog.get_distinct_id()`.
142
152
 
143
- The "not loaded yet" return value varies across SDKs – some return `undefined`/`nil`/`None`, others return `false` or a `defaultValue` you provide. Don't assume that a falsy return means the flag is off. Check your SDK's documentation for the exact return type of `getFeatureFlag()` and `isFeatureEnabled()` when flags haven't loaded, and handle that state explicitly. If your goal is to programmatically check whether a flag exists at all, use the [Feature Flags API](/docs/api/feature-flags.md) to query flag definitions directly.
153
+ The "not loaded yet" return value varies across SDKs – some return `undefined`/`nil`/`None`, others return `false` or a `defaultValue` you provide. Don't assume that a falsy return means the flag is off. Check your SDK's documentation for the exact return type of `getFeatureFlag()` and `isFeatureEnabled()` when flags haven't loaded, and handle that state explicitly. If your goal is to programmatically check whether a flag exists at all, use the [Feature Flags API](https://posthog.com/docs/api/feature-flags.md) to query flag definitions directly.
144
154
 
145
155
  ## Flag hygiene
146
156
 
@@ -148,7 +158,7 @@ Flags are infrastructure. Like any infrastructure, they accumulate cost when lef
148
158
 
149
159
  ### Choose a flag type intentionally
150
160
 
151
- Every flag in PostHog is configured as client-side, server-side, or both via [evaluation contexts](/docs/feature-flags/evaluation-contexts.md). New flags default to "server and client" – this exists for backwards compatibility (it's how all flags worked before we added evaluation contexts) and to avoid blocking users who haven't thought about their implementation yet. It's a safe starting point, not a recommendation.
161
+ Every flag in PostHog is configured as client-side, server-side, or both via [evaluation contexts](https://posthog.com/docs/feature-flags/evaluation-contexts.md). New flags default to "server and client" – this exists for backwards compatibility (it's how all flags worked before we added evaluation contexts) and to avoid blocking users who haven't thought about their implementation yet. It's a safe starting point, not a recommendation.
152
162
 
153
163
  If all your flags are set to both, that usually means the decision was never revisited after creation – and you're paying for client-side evaluation on flags that only need to exist on your server.
154
164
 
@@ -158,7 +168,7 @@ Pick the context based on where the flag is actually consumed. Server-side flags
158
168
 
159
169
  A flag set to 100% of all users with no property targeting is a flag that has finished its job. It's always returning the same value – the rollout is complete, the experiment concluded, the feature is live. If your SDK still evaluates that flag, it can keep making billable `/flags` requests, keep appearing in SDK payloads, and add clutter to your codebase.
160
170
 
161
- Remove the flag and hardcode the winning path. If you're not ready to remove it from code, at least archive it in PostHog so it stops being evaluated. Stale flags are the most common source of unnecessary flag evaluation. See [cleaning up stale flags](/docs/feature-flags/cleaning-up-stale-flags.md) for the full workflow and [cutting costs](/docs/feature-flags/cutting-costs.md) for more on reducing your bill.
171
+ Remove the flag and hardcode the winning path. If you're not ready to remove it from code, at least archive it in PostHog so it stops being evaluated. Stale flags are the most common source of unnecessary flag evaluation. See [cleaning up stale flags](https://posthog.com/docs/feature-flags/cleaning-up-stale-flags.md) for the full workflow and [cutting costs](https://posthog.com/docs/feature-flags/cutting-costs.md) for more on reducing your bill.
162
172
 
163
173
  **An idea worth considering:** design your flag code paths with an escape hatch you control outside of PostHog. For example, a "gate flag" that your server reads once every 30 seconds (not per user) – when it's `true`, the feature is fully rolled out and your code skips the per-user flag evaluation entirely. This means you stop making per-user `/flags` requests for that rollout as soon as it's complete, even before you remove the flag from code. And you can dial it back by setting the gate flag to `false`. This is also another application of "evaluate once, not continuously" – if you cache flag results, your per-user evaluation cost drops while you wait for the code cleanup.
164
174
 
@@ -170,14 +180,12 @@ This is the practical extension of "evaluate once, not continuously." Your serve
170
180
 
171
181
  ### Use a reverse proxy
172
182
 
173
- Ad blockers can disable your Feature Flags, leading to users seeing the wrong version of your app or missing a rollout. Deploy a [reverse proxy](/docs/advanced/proxy.md) so requests go through your own domain. PostHog offers a free [managed reverse proxy](/docs/advanced/proxy/managed-reverse-proxy.md), or you can run your own.
183
+ Ad blockers can disable your Feature Flags, leading to users seeing the wrong version of your app or missing a rollout. Deploy a [reverse proxy](https://posthog.com/docs/advanced/proxy.md) so requests go through your own domain. PostHog offers a free [managed reverse proxy](https://posthog.com/docs/advanced/proxy/managed-reverse-proxy.md), or you can run your own.
174
184
 
175
185
  ### Call your flag in as few places as possible
176
186
 
177
187
  The more locations a flag appears in your code, the more likely it is to cause problems – a developer removes it in one place but forgets another. If you use a flag in multiple places, wrap it in a single function:
178
188
 
179
- JavaScript
180
-
181
189
  ```javascript
182
190
  function useBetaFeature() {
183
191
  return posthog.isFeatureEnabled('beta-feature')
@@ -195,11 +203,11 @@ Good naming makes flags easier to understand and maintain:
195
203
 
196
204
  ### Roll out progressively
197
205
 
198
- Start at 5-10% of users, monitor metrics, then gradually increase. This is a [phased rollout](/tutorials/phased-rollout.md). At PostHog, we typically roll out to the developer first, then the internal team, then beta users, then everyone.
206
+ Start at 5-10% of users, monitor metrics, then gradually increase. This is a [phased rollout](https://posthog.com/tutorials/phased-rollout.md). At PostHog, we typically roll out to the developer first, then the internal team, then beta users, then everyone.
199
207
 
200
208
  ### Use dependencies for complex rollouts
201
209
 
202
- [Feature flag dependencies](/docs/feature-flags/dependencies.md) let one flag's activation depend on another flag's state – useful for enabling complex features only after foundational components are active, or running Experiments only on users with specific features enabled. Keep dependency chains simple and avoid circular dependencies.
210
+ [Feature flag dependencies](https://posthog.com/docs/feature-flags/dependencies.md) let one flag's activation depend on another flag's state – useful for enabling complex features only after foundational components are active, or running Experiments only on users with specific features enabled. Keep dependency chains simple and avoid circular dependencies.
203
211
 
204
212
  ### Be careful with "Latest" person properties
205
213
 
@@ -207,19 +215,11 @@ PostHog automatically creates person properties like "Latest Current URL" and "L
207
215
 
208
216
  ### Reducing your bill
209
217
 
210
- Stale flags are the most common source of unnecessary cost. Beyond cleaning up flags, see our [dedicated guide to cutting costs](/docs/feature-flags/cutting-costs.md) for estimating and reducing your feature flag bill.
218
+ Stale flags are the most common source of unnecessary cost. Beyond cleaning up flags, see our [dedicated guide to cutting costs](https://posthog.com/docs/feature-flags/cutting-costs.md) for estimating and reducing your feature flag bill.
211
219
 
212
220
  ## Further reading
213
221
 
214
- - [Identity resolution](/docs/product-analytics/identity-resolution.md) – how PostHog resolves who a user is
215
- - [Keeping flag evaluations stable](/docs/feature-flags/stable-identity-for-flags.md) – preventing the hash input from changing across auth transitions
216
- - [Local evaluation](/docs/feature-flags/local-evaluation.md) – server-side evaluation for explicit input control
217
- - [Bootstrapping](/docs/feature-flags/bootstrapping.md) – having flag values before the page renders
218
-
219
- ### Still have questions?
220
-
221
- Ask PostHog AI
222
-
223
- ### Was this page useful?
224
-
225
- HelpfulCould be better
222
+ - [Identity resolution](https://posthog.com/docs/product-analytics/identity-resolution.md) – how PostHog resolves who a user is
223
+ - [Keeping flag evaluations stable](https://posthog.com/docs/feature-flags/stable-identity-for-flags.md) – preventing the hash input from changing across auth transitions
224
+ - [Local evaluation](https://posthog.com/docs/feature-flags/local-evaluation.md) – server-side evaluation for explicit input control
225
+ - [Bootstrapping](https://posthog.com/docs/feature-flags/bootstrapping.md) – having flag values before the page renders
@@ -2,18 +2,29 @@
2
2
 
3
3
  # Django
4
4
 
5
+ Canonical URL: https://posthog.com/docs/libraries/django
6
+
7
+ > **Cloud region:** This page uses US Cloud hosts. For an EU Cloud project, use `https://eu.i.posthog.com` instead of `https://us.i.posthog.com` (ingestion host), and use `https://eu.posthog.com` instead of `https://app.posthog.com` (app host for links).
8
+
9
+ ## On this page
10
+
11
+ - [Beta: integration via LLM](#beta-integration-via-llm)
12
+ - [Installation](#installation)
13
+ - [Identifying users](#identifying-users)
14
+ - [Django contexts middleware](#django-contexts-middleware)
15
+ - [Next steps](#next-steps)
16
+ - [Supported versions](#supported-versions)
17
+
5
18
  PostHog makes it easy to get data about traffic and usage of your Django app. Integrating PostHog enables analytics, custom events capture, feature flags, error tracking, and more.
6
19
 
7
- This guide walks you through integrating PostHog into your Django app using the [Python SDK](/docs/libraries/python.md).
20
+ This guide walks you through integrating PostHog into your Django app using the [Python SDK](https://posthog.com/docs/libraries/python.md).
8
21
 
9
22
  ## Beta: integration via LLM
10
23
 
11
- Install PostHog for Django in seconds with our wizard by running this prompt with [LLM coding agents](/blog/envoy-wizard-llm-agent.md) like Cursor and Bolt, or by running it in your terminal.
24
+ Install PostHog for Django in seconds with our wizard by running this prompt with [LLM coding agents](https://posthog.com/blog/envoy-wizard-llm-agent.md) like Cursor and Bolt, or by running it in your terminal.
12
25
 
13
26
  `npx @posthog/wizard`
14
27
 
15
- [Learn more](/wizard.md)
16
-
17
28
  Or, to integrate manually, continue with the rest of this guide.
18
29
 
19
30
  > These docs cover version `7.x` of the Python SDK, which requires Python 3.10 or higher. On Python 3.9? See [supported versions](#supported-versions).
@@ -53,8 +64,6 @@ You can find your project token and instance address in [your project settings](
53
64
 
54
65
  To capture events from any file, import `posthog` and call the method you need. For example:
55
66
 
56
- Python
57
-
58
67
  ```python
59
68
  import posthog
60
69
  from posthog import identify_context
@@ -69,7 +78,7 @@ def some_request(request):
69
78
  posthog.capture('event_name')
70
79
  ```
71
80
 
72
- Events captured without a context or explicit `distinct_id` are sent as [anonymous events](/docs/data/anonymous-vs-identified-events.md) with an auto-generated `distinct_id`. See the [Python SDK docs](/docs/libraries/python.md#person-profiles-and-properties) for more details.
81
+ Events captured without a context or explicit `distinct_id` are sent as [anonymous events](https://posthog.com/docs/data/anonymous-vs-identified-events.md) with an auto-generated `distinct_id`. See the [Python SDK docs](https://posthog.com/docs/libraries/python.md#person-profiles-and-properties) for more details.
73
82
 
74
83
  ## Identifying users
75
84
 
@@ -77,8 +86,6 @@ Events captured without a context or explicit `distinct_id` are sent as [anonymo
77
86
  >
78
87
  > In Python, you can do this through a context. All event captures in the same context will be tagged automatically with the correct `distinct_id`. Typically, you would set a fresh context and identify at the top of each route.
79
88
  >
80
- > Python
81
- >
82
89
  > ```python
83
90
  > from posthog import new_context, identify_context, capture
84
91
  >
@@ -94,18 +101,16 @@ Events captured without a context or explicit `distinct_id` are sent as [anonymo
94
101
 
95
102
  ## Django contexts middleware
96
103
 
97
- The Python SDK provides a Django middleware that automatically wraps all requests with a [context](/docs/libraries/python.md#contexts). This middleware extracts session and user information from each request and tags all events captured during that request with relevant metadata.
104
+ The Python SDK provides a Django middleware that automatically wraps all requests with a [context](https://posthog.com/docs/libraries/python.md#contexts). This middleware extracts session and user information from each request and adds relevant properties to events captured during that request.
98
105
 
99
106
  ### Basic setup
100
107
 
101
108
  Add the middleware to your Django settings. If your app uses Django authentication, place it after `django.contrib.auth.middleware.AuthenticationMiddleware` so the middleware can use the authenticated Django user as a distinct ID fallback and capture the user's email.
102
109
 
103
- Python
104
-
105
110
  ```python
106
111
  MIDDLEWARE = [
107
112
  # ... other middleware
108
- 'posthog.integrations.django.PosthogContextMiddleware',
113
+ "posthog.integrations.django.PosthogContextMiddleware",
109
114
  # ... other middleware
110
115
  ]
111
116
  ```
@@ -114,8 +119,8 @@ The middleware uses the globally configured `posthog` client by default, so you
114
119
 
115
120
  The middleware automatically extracts and uses:
116
121
 
117
- - **Session ID** from the `X-POSTHOG-SESSION-ID` header, if present
118
- - **Distinct ID** from the `X-POSTHOG-DISTINCT-ID` header, if present, falling back to the authenticated Django user's `pk` (Django's primary-key alias, which works with custom user models)
122
+ - **Session ID** from the `X-POSTHOG-SESSION-ID` header when you enable `POSTHOG_MW_TRUST_TRACING_HEADERS`
123
+ - **Distinct ID** from the authenticated Django user's `pk` (Django's primary-key alias, which works with custom user models), or from the `X-POSTHOG-DISTINCT-ID` header when you enable `POSTHOG_MW_TRUST_TRACING_HEADERS`
119
124
  - **User email** from the authenticated Django user's `email` as `email`
120
125
  - **Current URL** as `$current_url`
121
126
  - **Request method** as `$request_method`
@@ -123,7 +128,14 @@ The middleware automatically extracts and uses:
123
128
  - **Forwarded IP address** from `X-Forwarded-For` as `$ip`
124
129
  - **User agent** from `User-Agent` as `$user_agent`
125
130
 
126
- The session and distinct ID headers are sanitized before use. Empty values are ignored, control characters are removed, values are trimmed, and values are capped at 1000 characters.
131
+ The middleware ignores the session and distinct ID headers by default because clients can control them. To use them as analytics context, enable them in your Django settings:
132
+
133
+ ```python
134
+ # settings.py
135
+ POSTHOG_MW_TRUST_TRACING_HEADERS = True
136
+ ```
137
+
138
+ Don't use these headers for authentication or authorization. The middleware sanitizes trusted header values before use. It ignores empty values, removes control characters, trims values, and caps them at 1000 characters.
127
139
 
128
140
  All events captured during the request (including exceptions) include these properties and are associated with the extracted session and distinct ID.
129
141
 
@@ -133,8 +145,6 @@ The middleware reads `request.user` once, before your view runs. On a login or s
133
145
 
134
146
  Identify the context from inside the request once you know who the user is. Django's auth signals are the natural place:
135
147
 
136
- Python
137
-
138
148
  ```python
139
149
  from django.contrib.auth.signals import user_logged_in
140
150
  from django.dispatch import receiver
@@ -147,7 +157,7 @@ def identify_posthog_user(sender, request, user, **kwargs):
147
157
 
148
158
  Every capture later in that request is then attributed to the user who just logged in. Requests made after login don't need this. The middleware sees the authenticated user from the start.
149
159
 
150
- If you're using [PostHog JavaScript Web](/docs/libraries/js.md) on the frontend, configure [`tracing_headers`](/docs/libraries/js/config.md#tracing-headers) for your Django backend hostname so browser requests include the session and distinct ID headers.
160
+ If you're using [PostHog JavaScript Web](https://posthog.com/docs/libraries/js.md) on the frontend, configure [`tracing_headers`](https://posthog.com/docs/libraries/js/config.md#tracing-headers) for your Django backend hostname so browser requests include the session and distinct ID headers. You must also set `POSTHOG_MW_TRUST_TRACING_HEADERS = True` in Django to accept them.
151
161
 
152
162
  ### Exception capture
153
163
 
@@ -155,120 +165,166 @@ By default, the middleware captures exceptions and sends them to PostHog's error
155
165
 
156
166
  Disable this by setting:
157
167
 
158
- Python
159
-
160
168
  ```python
161
169
  # settings.py
162
170
  POSTHOG_MW_CAPTURE_EXCEPTIONS = False
163
171
  ```
164
172
 
165
- ### Adding custom tags
173
+ Set `POSTHOG_MW_CAPTURE_EXCEPTIONS = None` to inherit the global client's `enable_exception_autocapture` setting. When you omit `POSTHOG_MW_CAPTURE_EXCEPTIONS`, the middleware preserves the existing Django default and captures exceptions.
174
+
175
+ ### Capture Django REST Framework exceptions
166
176
 
167
- Use `POSTHOG_MW_EXTRA_TAGS` to add custom properties to all requests:
177
+ This integration requires version `7.69.0` or later of the Python SDK.
168
178
 
169
- Python
179
+ Django REST Framework (DRF) converts many exceptions into responses before Django middleware can observe them. Add PostHog's DRF exception handler to capture these handled errors:
170
180
 
171
181
  ```python
172
182
  # settings.py
173
- def add_user_tags(request):
183
+ REST_FRAMEWORK = {
184
+ # ... other DRF settings
185
+ "EXCEPTION_HANDLER": "posthog.integrations.drf.exception_handler",
186
+ }
187
+ ```
188
+
189
+ Keep `PosthogContextMiddleware` enabled. The middleware adds request properties and identity to the active context, while the DRF handler captures handled API exceptions. The handler captures responses with a `5xx` status by default and ignores expected `4xx` responses. If DRF doesn't handle an exception, Django re-raises it and the middleware captures it instead.
190
+
191
+ If you already use a custom DRF exception handler, wrap it in an application module:
192
+
193
+ ```python
194
+ # your_app/exception_handlers.py
195
+ from posthog.integrations.drf import create_exception_handler
196
+ from your_app.api import existing_exception_handler
197
+
198
+ exception_handler = create_exception_handler(existing_exception_handler)
199
+ ```
200
+
201
+ Then point DRF to the wrapper:
202
+
203
+ ```python
204
+ # settings.py
205
+ REST_FRAMEWORK = {
206
+ "EXCEPTION_HANDLER": "your_app.exception_handlers.exception_handler",
207
+ }
208
+ ```
209
+
210
+ To capture selected `4xx` responses, create a handler with `capture_4xx=True` and an exception filter:
211
+
212
+ ```python
213
+ # your_app/exception_handlers.py
214
+ from posthog.integrations.drf import create_exception_handler
215
+
216
+
217
+ def capture_auth_errors(exception, response, context):
218
+ return response.status_code >= 500 or response.status_code in {401, 403}
219
+
220
+
221
+ exception_handler = create_exception_handler(
222
+ capture_4xx=True,
223
+ exception_filter=capture_auth_errors,
224
+ )
225
+ ```
226
+
227
+ The DRF handler follows `POSTHOG_MW_CAPTURE_EXCEPTIONS` and `POSTHOG_MW_REQUEST_FILTER`. You can also pass `capture_exceptions=True` or `capture_exceptions=False` to `create_exception_handler()` to override the middleware setting.
228
+
229
+ ### Add custom properties
230
+
231
+ Use `POSTHOG_MW_EXTRA_PROPERTIES` to add custom properties to all requests:
232
+
233
+ ```python
234
+ # settings.py
235
+ def add_user_properties(request):
174
236
  # type: (HttpRequest) -> Dict[str, Any]
175
- tags = {}
176
- if hasattr(request, 'user') and request.user.is_authenticated:
237
+ properties = {}
238
+ if hasattr(request, "user") and request.user.is_authenticated:
177
239
  # Use pk instead of id so this works with custom User primary keys.
178
- tags['user_id'] = str(request.user.pk)
179
- tags['email'] = request.user.email
180
- return tags
240
+ properties["user_id"] = str(request.user.pk)
241
+ properties["email"] = request.user.email
242
+ return properties
181
243
 
182
- POSTHOG_MW_EXTRA_TAGS = add_user_tags
244
+ POSTHOG_MW_EXTRA_PROPERTIES = add_user_properties
183
245
  ```
184
246
 
185
- #### Filtering requests
247
+ ### Filter requests
186
248
 
187
249
  Skip tracking for certain requests using `POSTHOG_MW_REQUEST_FILTER`:
188
250
 
189
- Python
190
-
191
251
  ```python
192
252
  # settings.py
193
253
  def should_track_request(request):
194
254
  # type: (HttpRequest) -> bool
195
255
  # Don't track health checks or admin requests
196
- if request.path.startswith('/health') or request.path.startswith('/admin'):
256
+ if request.path.startswith("/health") or request.path.startswith("/admin"):
197
257
  return False
198
258
  return True
199
259
 
200
260
  POSTHOG_MW_REQUEST_FILTER = should_track_request
201
261
  ```
202
262
 
203
- ### Modifying default tags
263
+ ### Modify default properties
204
264
 
205
- Use `POSTHOG_MW_TAG_MAP` to modify or remove default tags:
206
-
207
- Python
265
+ Use `POSTHOG_MW_PROPERTIES_MAP` to modify or remove default properties:
208
266
 
209
267
  ```python
210
268
  # settings.py
211
- def customize_tags(tags):
269
+ def customize_properties(properties):
212
270
  # type: (Dict[str, Any]) -> Dict[str, Any]
213
271
  # Remove URL for privacy
214
- tags.pop('$current_url', None)
272
+ properties.pop("$current_url", None)
215
273
  # Add custom prefix to method
216
- if '$request_method' in tags:
217
- tags['http_method'] = tags.pop('$request_method')
218
- return tags
274
+ if "$request_method" in properties:
275
+ properties["http_method"] = properties.pop("$request_method")
276
+ return properties
219
277
 
220
- POSTHOG_MW_TAG_MAP = customize_tags
278
+ POSTHOG_MW_PROPERTIES_MAP = customize_properties
221
279
  ```
222
280
 
223
281
  ### Complete configuration example
224
282
 
225
- Python
226
-
227
283
  ```python
228
284
  # settings.py
229
285
  def add_request_context(request):
230
286
  # type: (HttpRequest) -> Dict[str, Any]
231
- tags = {}
232
- if hasattr(request, 'user') and request.user.is_authenticated:
233
- tags['user_type'] = 'authenticated'
287
+ properties = {}
288
+ if hasattr(request, "user") and request.user.is_authenticated:
289
+ properties["user_type"] = "authenticated"
234
290
  # Use pk instead of id so this works with custom User primary keys.
235
- tags['user_id'] = str(request.user.pk)
291
+ properties["user_id"] = str(request.user.pk)
236
292
  else:
237
- tags['user_type'] = 'anonymous'
293
+ properties["user_type"] = "anonymous"
238
294
 
239
295
  # Add request info
240
- tags['user_agent'] = request.META.get('HTTP_USER_AGENT', '')
241
- return tags
296
+ properties["user_agent"] = request.META.get("HTTP_USER_AGENT", "")
297
+ return properties
242
298
 
243
299
  def filter_tracking(request):
244
300
  # type: (HttpRequest) -> bool
245
301
  # Skip internal endpoints
246
- return not request.path.startswith(('/health', '/metrics', '/admin'))
302
+ return not request.path.startswith(("/health", "/metrics", "/admin"))
247
303
 
248
- def clean_tags(tags):
304
+ def clean_properties(properties):
249
305
  # type: (Dict[str, Any]) -> Dict[str, Any]
250
306
  # Remove sensitive data
251
- tags.pop('user_agent', None)
252
- return tags
307
+ properties.pop("user_agent", None)
308
+ return properties
253
309
 
254
- POSTHOG_MW_EXTRA_TAGS = add_request_context
310
+ POSTHOG_MW_EXTRA_PROPERTIES = add_request_context
255
311
  POSTHOG_MW_REQUEST_FILTER = filter_tracking
256
- POSTHOG_MW_TAG_MAP = clean_tags
312
+ POSTHOG_MW_PROPERTIES_MAP = clean_properties
257
313
  POSTHOG_MW_CAPTURE_EXCEPTIONS = True
258
314
  ```
259
315
 
260
- All events captured within the request context automatically include the configured tags and are associated with the session and user identified from the request headers or Django authentication.
316
+ All events captured within the request context automatically include the configured properties. They use the authenticated Django user as the distinct ID, or the session and distinct ID from request headers when you enable `POSTHOG_MW_TRUST_TRACING_HEADERS`.
261
317
 
262
318
  The middleware supports both sync (WSGI) and async (ASGI) Django applications. In async mode, it uses Django's `request.auser()` API when available to avoid synchronous user access.
263
319
 
264
320
  ## Next steps
265
321
 
266
- For any technical questions for how to integrate specific PostHog features into Django (such as analytics, feature flags, A/B testing, etc.), have a look at our [Python SDK docs](/docs/libraries/python.md).
322
+ For any technical questions for how to integrate specific PostHog features into Django (such as analytics, feature flags, A/B testing, etc.), have a look at our [Python SDK docs](https://posthog.com/docs/libraries/python.md).
267
323
 
268
324
  Alternatively, the following tutorials can help you get started:
269
325
 
270
- - [Setting up Django analytics, feature flags, and more](/tutorials/django-analytics.md)
271
- - [How to set up A/B tests in Django](/tutorials/django-ab-tests.md)
326
+ - [Setting up Django analytics, feature flags, and more](https://posthog.com/tutorials/django-analytics.md)
327
+ - [How to set up A/B tests in Django](https://posthog.com/tutorials/django-ab-tests.md)
272
328
 
273
329
  ## Supported versions
274
330
 
@@ -277,11 +333,3 @@ These docs cover version `7.x` of the PostHog Python SDK, which requires Python
277
333
  Everything on this page works the same way on `6.9.3`. Event capture, the context API (`new_context`, `identify_context`, `set_context_session`), and `PosthogContextMiddleware` are identical on `6.9.3` and `7.0.0` — `7.0.0` only dropped Python 3.9 and bumped the optional LLM provider SDKs. That includes the middleware identifying the request context from the `X-POSTHOG-DISTINCT-ID` header and falling back to the authenticated user, which behaves the same across both lines.
278
334
 
279
335
  Later `7.x` releases add what the 6.x line does not receive, such as the Celery integration, tracing header sanitization, and `set_context_device_id`. They also changed the middleware's own captured properties: `7.x` sends the request IP as `$ip`, where `6.9.3` sends it as `$ip_address`, and `7.x` additionally captures `$request_path`, `$raw_user_agent`, and the authenticated user's `email`.
280
-
281
- ### Still have questions?
282
-
283
- Ask PostHog AI
284
-
285
- ### Was this page useful?
286
-
287
- HelpfulCould be better