pixellint

CAPI 200 is not conversion validation

Short answer

Vendors optimize the ingest path for throughput. Schema checks are shallow, asynchronous, or missing. PostHog documents that a missing event or distinct_id still returns 200 and is not ingested. Segment's HTTP 200 does not prove track had an event name.

Validate CAPI JSON Open the rulepack

CAPI 200 is liveness

CAPI 200 is liveness. Meta CAPI and Facebook CAPI can return 200 with Test Events empty. A conversion API validator checks CAPI JSON. Status code is not that check. CAPI not working is usually the body, not the token.

PostHog and Segment document the quiet drop

PostHog capture: missing event or distinct_id still HTTP 200, not ingested. vendor.posthog.body.event.missing and vendor.posthog.body.distinct_id.missing exist because the official docs say the quiet drop is expected. timestamp must be ISO 8601; an epoch is read as ingestion time (vendor.posthog.body.timestamp.invalid).

Segment HTTP Tracking API: a track in a batch needs an event name (vendor.segment.body.track_requires_an_event_name). Every call needs userId or anonymousId (vendor.segment.body.call_needs_an_identifier). timestamp is ISO 8601 (vendor.segment.body.timestamp.invalid). The 200 does not check these. Your retry logic that keys on non-2xx will never see the miss.

GA4 204 is the same lie in another status

Measurement Protocol collect returns HTTP 204. That is 'accepted for processing', not 'your purchase had items'. vendor.google-analytics.body.purchase_requires_ecommerce_fields is currency, value, transaction_id, and items. timestamp_micros that is 13 digits is milliseconds, not microseconds (vendor.google-analytics.body.timestamp_micros.invalid). The 204 will not mention it.

The debug endpoint is where Google will talk back. Production collect will store a misspelled event name as a custom event and move on. Use /debug/mp/collect in CI, then send the same body to prod without debug_mode.

Ads CAPIs return 200 with errors in the body

Meta often returns 200 with an error object in the JSON, or accepts the batch and reports per-event errors later. Read the JSON. A gateway 200 on your proxy in front of CAPI is even less information: it means nginx was up.

access_token missing is vendor.meta-conversions-api.param.access_token.missing if you lint the request. If you only log status codes, a 200 with messages[].error is a silent miss. TikTok and Snap behave like other high-throughput ingest: do not assume a 2xx mapped every field.

What to assert instead

Required fields, types, timestamp units, hashed versus raw, ISO currency. That is what Pixellint rulepacks encode from vendor docs. Run them in CI on fixtures, and on a sampled live payload if you can do it without shipping PII to a laptop.

Assert event_name, event_time digits, action_source enum, user_data present, event_id present on hybrid setups, Purchase value and currency. Assert PostHog event plus distinct_id. Assert Segment track event plus userId or anonymousId. Status code is not on that list.

Dashboards lag, and they graph the wrong name

Even a well-formed event can take minutes to hours to show. Absence in the UI is not a 404. Presence in the UI is not proof the payload matched the contract you intended. Purchse still graphs. It graphs as a custom event with no value schema.

CI on the JSON is the fast signal. Events Manager is the slow one. Using the slow one as your only test is how a timestamp-unit bug lives for a week while ROAS looks 'a bit off'.

Lint the body, ignore the smile

A capture without distinct_id still 200s. A Purchase without currency still 204s. The contract is the fields.

pixellint validate json '{"event":"order_completed"}' --rulepack vendor/posthog
# error  vendor.posthog.body.distinct_id.missing
# (HTTP 200 from /capture does not report this)

pixellint validate json '{"type":"track","userId":"u1"}' --rulepack vendor/segment
# error  vendor.segment.body.track_requires_an_event_name

pixellint validate json @ga4.json --rulepack vendor/google-analytics
# error  vendor.google-analytics.body.timestamp_micros.invalid
# when timestamp_micros is 13 digits. Collect still returns 204.

Questions

Does CAPI 200 mean the conversion API worked?

No. CAPI 200 is liveness. Meta CAPI and Facebook CAPI can return 200 with per-event errors, or accept a 13-digit event_time and never attribute. CAPI not working is usually the body, not the token.

Is HTTP 200 conversion validation?

No. A conversion API validator checks CAPI JSON against the vendor contract. Status code is not that check. PostHog and Segment document 200s for payloads they will not ingest. GA4 Measurement Protocol collect returns 204.

Why does Facebook CAPI return 200 if Test Events is empty?

Graph ingest is not a schema review. Count event_time digits, hash em, leave IP in the clear, set event_source_url for website. Then paste the body into a CAPI tester. Rotating the token is the expensive debug.

Check the artifact

Paste the pixel URL or JSON body into the playground. Same engine as pixellint validate. Artifacts you test may be stored; see privacy.