|
| 1 | +# Migrating from posthog 7.x to 8.0 |
| 2 | + |
| 3 | +posthog 8.0 sends every event through capture v1 and removes the legacy capture path. |
| 4 | +Most apps upgrade without code changes. |
| 5 | +Read the checklist first, then the sections that apply to you. |
| 6 | + |
| 7 | +## Checklist |
| 8 | + |
| 9 | +You need to change code if your app does any of these: |
| 10 | + |
| 11 | +- passes `Client` or `AsyncPosthog` constructor arguments by position after `host` |
| 12 | +- sets `capture_mode`, `POSTHOG_CAPTURE_MODE` or `gzip` |
| 13 | +- imports `CaptureV1Error`, `posthog.capture_v1`, `request.batch_post`, `EVENTS_ENDPOINT` or `AI_EVENTS_ENDPOINT` |
| 14 | +- sends events to a self-hosted PostHog that does not serve the capture v1 endpoints |
| 15 | +- reuses one event `uuid` for more than one event |
| 16 | +- sets `$process_person_profile` to turn person processing on for events without a distinct ID |
| 17 | +- passes strings such as `"true"` for `$cookieless_mode`, `$ignore_sent_at` or `$process_person_profile` |
| 18 | +- expects `super_properties` to override properties passed to a single call |
| 19 | +- tests the AI integrations with a mock client and asserts on `capture` |
| 20 | +- passes its own client object to the AI integrations |
| 21 | + |
| 22 | +## Endpoints |
| 23 | + |
| 24 | +| Method | Endpoint | |
| 25 | +| --- | --- | |
| 26 | +| `capture`, `set`, `set_once`, `alias`, `group_identify`, `capture_exception` | `/i/v1/analytics/events` | |
| 27 | +| `capture_ai`, `capture_ai_immediate`, and every built-in AI integration | `/i/v1/ai/events` | |
| 28 | + |
| 29 | +If you send events to a self-hosted PostHog, check that it serves both endpoints before you upgrade. |
| 30 | +An endpoint that is not served drops every event sent to it. |
| 31 | + |
| 32 | +## Removed options and APIs |
| 33 | + |
| 34 | +| Removed | Use instead | |
| 35 | +| --- | --- | |
| 36 | +| `capture_mode`, `CaptureMode`, `POSTHOG_CAPTURE_MODE` | Nothing. Capture v1 is the only path. | |
| 37 | +| `gzip=True` | `capture_compression=CaptureCompression.GZIP`. The default is no compression. `POSTHOG_CAPTURE_COMPRESSION` still works. | |
| 38 | +| `CaptureV1Error` | `CaptureError`. There is no alias. | |
| 39 | +| `posthog.capture_v1` | `posthog.capture_event` and `posthog.capture_send` | |
| 40 | +| `request.batch_post`, `async_batch_post`, `EVENTS_ENDPOINT`, `AI_EVENTS_ENDPOINT` | Nothing. Send events through a client. | |
| 41 | +| The `gzip` parameter of `request.post` and `request.flags` | Nothing. | |
| 42 | +| The `backoff` dependency | Add it to your own requirements if your code imports it. | |
| 43 | + |
| 44 | +Constructor arguments after `host` are keyword-only: |
| 45 | + |
| 46 | +```python |
| 47 | +# 7.x |
| 48 | +Posthog("<api_key>", "https://us.i.posthog.com", True) |
| 49 | + |
| 50 | +# 8.0 |
| 51 | +Posthog("<api_key>", host="https://us.i.posthog.com", debug=True) |
| 52 | +``` |
| 53 | + |
| 54 | +## Errors and `on_error` |
| 55 | + |
| 56 | +Capture v1 returns a result for every event, even when the request succeeds. |
| 57 | +An event can be dropped, for example by billing limits or quotas, inside a 2xx response. |
| 58 | +8.0 reports those events as failures. |
| 59 | + |
| 60 | +- Every capture failure is a `CaptureError`. It has these fields: |
| 61 | + - `status`: the HTTP status, or `0` when the request never got a response |
| 62 | + - `endpoint`, `request_id` and `attempts` |
| 63 | + - `drops` and `retry_exhausted`: the uuids that were dropped or ran out of retries |
| 64 | + - `event_results`: a `CaptureEventResult(result, details)` for each uuid, from `posthog.capture_send` |
| 65 | + - `verdict_summary()`: counts such as `drop/billing=1, retry/not_persisted=1` |
| 66 | +- `on_error(error, batch)` receives every failure, including in `sync_mode` and from `AsyncPosthog.capture_immediate`. |
| 67 | +- Without `on_error`, the SDK logs one line per failed batch, for example `2 event(s) not persisted by /i/v1/analytics/events: ...`. The line never contains event content or the server's response text. |
| 68 | +- In `sync_mode`, a failed `capture()` calls `on_error` and returns `None`. |
| 69 | +- If a capture inside `on_error` fails, the SDK logs the failure and does not call `on_error` again. |
| 70 | +- A `429` response is not retried. |
| 71 | +- Retries start at 100 ms and double each attempt, up to 30 seconds. The `Consumer` default is 3 retries, down from 10. |
| 72 | + |
| 73 | +## Event uuids |
| 74 | + |
| 75 | +- Each event needs its own uuid. Capture rejects a whole batch that contains the same uuid twice, so the other events in that batch are lost too. |
| 76 | +- The SDK accepts a uuid with hyphens, 32 hex digits, `{...}` braces or a `urn:uuid:` prefix, in any case. It sends the lowercase hyphenated form and returns that form from `capture`. |
| 77 | +- Any other value is replaced with a generated uuid, and the SDK logs an error. This applies to `AsyncPosthog` too. |
| 78 | +- Generated uuids are UUIDv7. |
| 79 | + |
| 80 | +## Event options |
| 81 | + |
| 82 | +Capture v1 sends processing options in an `options` object, next to `properties`. |
| 83 | + |
| 84 | +- `capture`, `capture_ai`, `set`, `set_once`, `alias`, `group_identify` and `capture_exception` take an `options` argument: |
| 85 | + |
| 86 | + ```python |
| 87 | + posthog.capture("signed_up", distinct_id="user-1", options={"cookieless_mode": True}) |
| 88 | + ``` |
| 89 | + |
| 90 | +- `super_options` sets options on every event, like `super_properties`. |
| 91 | +- `set_context_option(key, value)` sets an option for the current context, like `tag()`. |
| 92 | +- Options are sent as given. PostHog validates them. |
| 93 | +- The legacy properties `$cookieless_mode`, `$ignore_sent_at`, `$product_tour_id` and `$process_person_profile` still work. They move into options after `before_send`. Their values are no longer converted, so pass `True` or `False`, not `"true"`. |
| 94 | +- An option wins over its legacy property. A `None` option counts as unset. |
| 95 | + |
| 96 | +Values apply in this order, and each layer overrides the ones before it: |
| 97 | + |
| 98 | +1. options the SDK sets, such as turning off person processing for events without a distinct ID |
| 99 | +2. `super_options` and `super_properties` |
| 100 | +3. context options and tags |
| 101 | +4. the `options` and `properties` of the call |
| 102 | +5. `before_send` |
| 103 | + |
| 104 | +Two changes follow from this order: |
| 105 | + |
| 106 | +- Properties passed to a call now override `super_properties`. `super_properties` can no longer change `$lib`, `$lib_version` or `$geoip_disable`. |
| 107 | +- An event without a distinct ID gets `options.process_person_profile = false`. A `$process_person_profile: true` property no longer turns person processing back on. Set the option instead. |
| 108 | + |
| 109 | +## AI capture |
| 110 | + |
| 111 | +- `capture_ai` and every built-in AI integration send to `/i/v1/ai/events`. The AI endpoint accepts events up to 8 MiB. |
| 112 | +- `enable_full_ai_capture` now controls only content: string truncation and media redaction. It no longer chooses the endpoint. `_use_ai_lane` and `_enable_multimodal_capture` still work as aliases. |
| 113 | +- The AI integrations call your client's `capture_ai`, and pass `options=`. A client object without `capture_ai` gets `capture` calls instead. A custom client must accept the `options` keyword argument. |
| 114 | +- Tests that pass a `Mock` client to an AI integration must assert on `mock.capture_ai`, not `mock.capture`. |
| 115 | +- When an AI integration or MCP falls back to a trace, run or session ID, it turns person processing off with a per-event option. A `$process_person_profile: true` property no longer turns it back on. `before_send` can still change it. |
| 116 | +- MCP's `PostHogCaptureEvent` has an `options` key. |
| 117 | +- New `Client` and `AsyncPosthog` arguments for the AI lane: |
| 118 | + - `capture_ai_compression`: default none. `POSTHOG_CAPTURE_COMPRESSION` does not apply to it. |
| 119 | + - `capture_ai_max_queue_size`: default 1000 |
| 120 | + - `capture_ai_timeout`: default 30 seconds |
| 121 | + - `capture_ai_max_event_bytes`: default 8 MiB plus 64 KiB. You can only lower it. |
| 122 | +- `AsyncPosthog` has `capture_ai` and `capture_ai_immediate`. |
| 123 | + |
| 124 | +## Batching and size limits |
| 125 | + |
| 126 | +- An analytics event over 900 KiB is dropped and logged. This now applies in `sync_mode` too. |
| 127 | +- An AI event over `capture_ai_max_event_bytes` is dropped and logged. |
| 128 | +- A batch stops before an event that would take it past 5 MiB. A larger event is sent alone. |
| 129 | +- `flush()` and `shutdown()` drain the analytics and AI queues at the same time. |
| 130 | + |
| 131 | +## OpenFeature provider |
| 132 | + |
| 133 | +`openfeature-provider-posthog` 0.2.0 requires posthog 8.0 or later. |
| 134 | +Upgrade both packages together. |
0 commit comments