pixellint

Time · engineers · marketers · PMs

Too late for the graph, still true in finance

A CRM marks closed-won 12 days after the click: Meta CAPI will not attribute it, while Google Ads offline import may still take it depending on the click time and the conversion window. The money is real either way.

Vendor maximum age

Meta documents event_time as at most 7 days before the event is sent. Snap CAPI is also the last 7 days. LinkedIn conversionHappenedAt rejects events older than 90 days. A seconds timestamp on LinkedIn looks like 1970, which fails that check immediately. GA4 timestamp_micros can backdate about 72 hours. Older MP hits ingest without that historical event time.

The attribution window and the maximum upload age are not the same setting. An event can be young enough to ingest and still fall outside the click window, or old enough that the API drops it before reporting starts. Pixellint checks units (10 vs 13 vs 16 digits, ISO vs epoch). It does not compute age against wall clock at validate time in those packs. A 10-digit Meta stamp from 2020 is the right unit and still too old for the graph.

Wrong unit looks like late (or future)

A 10-digit value on LinkedIn is 1970 in milliseconds, older than 90 days, dropped. A 13-digit value on Meta is millennia ahead, outside the 7-day window, dropped. A 13-digit value on GA4 is milliseconds where microseconds were required, too small to be 'now', and not a valid timestamp_micros. Fix the unit before you debate CRM lag.

Pixellint codes: vendor.meta-conversions-api.body.event_time.invalid, vendor.linkedin-conversions-api.body.conversionHappenedAt.invalid, vendor.google-analytics.body.timestamp_micros.invalid, vendor.amplitude.body.time.invalid, vendor.reddit-conversions-api.body.event_at.invalid, vendor.pinterest-conversions-api.body.event_time.invalid.

Offline and CRM

Phone sales, POS, and Salesforce stages arrive late by nature. Send the time of the conversion, not the time of the CSV upload. Dedup against any lead pixel you already fired, using the original event_id or order id.

A 90-day CRM lag will silently miss Meta. Promise closed-won attribution only after you have checked the vendor's maximum event age, not after you have checked that Zapier returned 200. Google Ads offline conversions can still accept a click-dated conversion inside the conversion action window after Meta would have refused CAPI. That is why Ads and Meta disagree on the same closed-won row.

Two books

Keep the late events in the warehouse with honest timestamps. Report a finance ROAS that includes them. Do not force them into CAPI with event_time=now to sneak past the age check. That attributes Friday's store sales to Monday's campaigns.

Retries of a failed POST should reuse the original event_time and event_id. A nightly reconciler that resends last week's orders as new conversions is an incident, not a backfill. If Meta will not take them anymore, stop POSTing them to Meta. Load them in the warehouse. Tell finance which book is which.

ISO vendors still have a now-vs-then trap

PostHog, TikTok, and AppsFlyer will ingest an epoch-typed timestamp as arrival time. The event is not 'too late'. It is on time for today and wrong for the campaign day. That looks like a successful backfill in the HTTP log and a spike on the drain day in Insights. Send ISO 8601 (or AppsFlyer's UTC space-separated form) with the historical instant.

Pixellint is not affiliated with Meta, Google, LinkedIn, or Snap. Age caps live in those vendors' docs. The packs catch the unit mistakes that impersonate age failures.

// WRONG: sneak a 12-day-old Meta event through as now
event_time: Math.floor(Date.now() / 1000)

// RIGHT: honest time; if older than ~7 days, warehouse only for Meta
event_time: Math.floor(deal.closedAtMs / 1000)
// LinkedIn still wants 13-digit ms, 90-day cap
conversionHappenedAt: deal.closedAtMs

Check the artifact

Paste the pixel URL or JSON body into the playground. Same engine as pixellint validate. Nothing leaves the tab.