pixellint

vendor/posthog · vendor documented

PostHog capture treats an epoch timestamp as now

You backfill six months of events. Insights show a spike today. The events are not missing. They are stamped with the time PostHog received them.

timestamp is ISO 8601. An epoch number is a different type. Capture still accepts the request. The historical value is not applied. Missing event or distinct_id is not ingested, and PostHog still returns 200 OK. distinct_id is at most 200 characters. properties.distinct_id is accepted when the top-level field is absent. $create_alias needs properties.alias. $groupidentify needs $group_type and $group_key.

timestamp is ISO 8601, not epoch

Send 2026-07-26T06:00:00Z. Segment uses the same shape. Amplitude wants milliseconds. If your pipeline already emits epoch for Amplitude, do not reuse the field on PostHog.

Rule: vendor.posthog.body.timestamp.invalid

Every event needs distinct_id

On a single capture and on every row of batch. PostHog's own docs: a missing name, a missing distinct_id, or an empty distinct_id is not ingested, and you still get 200.

Rule: vendor.posthog.body.event_needs_an_identifier

What this pack matches

Hosts
posthog.com
Paths
…/e/…, …/capture/…, …/batch/…, …/i/v0/e…
Vendor docs
posthog.com/docs/api/capture

Rules

Codes are stable. A finding in CI, MCP, or the playground lands on the same id.

Field Required What it checks Rule ids Source
api_key required It is the project API key, and PostHog rejects the request without it. Fix: Send the project API key from your PostHog project settings. vendor.posthog.body.api_key.missing
vendor.posthog.body.api_key.empty
docs
historical_migration optional PostHog documents `historical_migration` as a boolean on `/batch`. Set it to `true` when backfilling so events are processed in order. vendor.posthog.body.historical_migration.empty
vendor.posthog.body.historical_migration.invalid
docs
event required It is the name of the event being captured. Fix: Name the event, such as `user signed up`. vendor.posthog.body.event.missing
vendor.posthog.body.event.empty
docs
distinct_id optional It identifies the person the event belongs to. PostHog documents at most 200 characters, and also reads `properties.distinct_id` when the top-level field is absent. Fix: Send `distinct_id` at the top level, at most 200 characters. vendor.posthog.body.distinct_id.empty
vendor.posthog.body.distinct_id.invalid
docs
properties.distinct_id optional PostHog reads `properties.distinct_id` when the top-level `distinct_id` is absent. Prefer the top-level field. Fix: Send `distinct_id` at the top level, or inside `properties` when the client cannot. vendor.posthog.body.properties.distinct_id.empty
vendor.posthog.body.properties.distinct_id.invalid
docs
timestamp optional PostHog documents the timestamp as ISO 8601. An epoch number is read as the ingestion time instead. Fix: Send an ISO 8601 timestamp, such as `2026-07-26T06:00:00Z`. vendor.posthog.body.timestamp.empty
vendor.posthog.body.timestamp.invalid
docs
properties.$ip optional It is the visitor IP, sent unhashed. vendor.posthog.body.properties.$ip.empty docs
properties.$process_person_profile optional PostHog documents this as a boolean. Set it to `false` to capture an anonymous event. vendor.posthog.body.properties.$process_person_profile.empty
vendor.posthog.body.properties.$process_person_profile.invalid
docs
properties.alias optional It is the distinct id being merged. PostHog documents it as required on `$create_alias`. Fix: Set `properties.alias` to the distinct id this person should merge into. vendor.posthog.body.properties.alias.empty docs
properties.$group_type optional It is the group type on `$groupidentify`. PostHog documents at most 400 characters. vendor.posthog.body.properties.$group_type.empty
vendor.posthog.body.properties.$group_type.invalid
docs
properties.$group_key optional It is the group key on `$groupidentify`. PostHog documents at most 400 characters. vendor.posthog.body.properties.$group_key.empty
vendor.posthog.body.properties.$group_key.invalid
docs
properties.$current_url optional It is the page URL or path on `$pageview`. PostHog documents it on the capture pageview example, including a path-only value. Fix: Send `properties.$current_url` as the page path or full URL, or drop the empty pair. vendor.posthog.body.properties.$current_url.empty docs
properties.$session_id optional It is the session id. PostHog documents it on the `$pageview` capture example. Fix: Send `properties.$session_id`, or drop the empty pair. vendor.posthog.body.properties.$session_id.empty docs
properties.$screen_name optional It is the screen name on `$screen`. PostHog documents it on the capture screen-view example. Fix: Send `properties.$screen_name`, or drop the empty pair. vendor.posthog.body.properties.$screen_name.empty docs
properties.$survey_id optional It identifies the survey. PostHog documents it as required on `survey sent`, `survey shown`, and `survey dismissed`. Fix: Set `properties.$survey_id` to the survey id from PostHog. vendor.posthog.body.properties.$survey_id.empty docs
body.event_needs_an_identifier required The event carries no `distinct_id`. PostHog documents it as required at the top level, and also reads `properties.distinct_id` when the top-level field is absent. A missing or empty id is not ingested, and the endpoint still returns 200. Fix: Send `distinct_id` at the top level. vendor.posthog.body.event_needs_an_identifier docs
body.hashed_plaintext_field required `properties.$ip` looks like a SHA-256 digest, but PostHog documents it as an IP address. Fix: Send the raw IP address. vendor.posthog.body.hashed_plaintext_field docs
body.alias_requires_alias required A `$create_alias` event carries no `properties.alias`. PostHog documents that field as the distinct id being merged. Fix: Set `properties.alias` to the distinct id this person should merge into. vendor.posthog.body.alias_requires_alias docs
body.groupidentify_requires_type_and_key required A `$groupidentify` event is missing `$group_type` or `$group_key`. PostHog documents both, each at most 400 characters. Fix: Set `properties.$group_type` and `properties.$group_key`. vendor.posthog.body.groupidentify_requires_type_and_key docs
body.survey_requires_survey_id required A survey event carries no `properties.$survey_id`. PostHog documents that field as required on `survey sent`, `survey shown`, and `survey dismissed`. Fix: Set `properties.$survey_id` to the survey id from PostHog. vendor.posthog.body.survey_requires_survey_id docs

Validate a payload

pixellint validate json @payload.json --rulepack vendor/posthog

Try this failing payload in the playground. PostHog capture with a hashed $ip.

{"api_key":"phc_abc123","event":"user signed up","distinct_id":"user-10024","timestamp":"2026-07-26T06:00:00Z","properties":{"$ip":"a85e9ca18f34935ab9b0381b25bfad2455444112b0149270fd88e3da172fe196"}}

cargo install pixellint · npm install pixellint