pixellint

Pixellint 0.43.0 checks AppsFlyer web events and visits.

Web and mobile S2S have distinct request contracts.

The existing AppsFlyer mobile pack inspects the mobile S2S operation on api3.appsflyer.com. Its required camelCase eventValue field is a string, and its identity and authentication follow that operation. A web event can have different valid fields without satisfying the mobile contract. Reusing the vendor name as the only selector would either miss the web operation or incorrectly apply mobile requirements to it.

The new web event and visit packs both belong to AppsFlyer. They identify separate operations on events.appsflyer.com and use the published web tables to check available request evidence. Their inventory maps 49 field contracts across the path, captured transport and native JSON body. This extends endpoint coverage for an already recognized vendor. It does not establish that every AppsFlyer product now has a complete contract.

Endpoint ownership also keeps a malformed request visible. A request to the web event route with an empty body object still belongs to that operation and can receive its required-field findings. The selector does not require a valid event name before it can recognize the destination. Otherwise, the very omission the pack is meant to detect would prevent the pack from running.

The captured destination chooses the event or visit pack.

The web event route is https://events.appsflyer.com/v2.0/s2s/inapps/app/web/{appId}. The visit route is https://events.appsflyer.com/v2.0/s2s/visits/app/web/{appId}. Both use POST. The final app component identifies the dashboard unified application ID. The published example begins with website-, but an example prefix is not enough evidence to reject another opaque application identifier.

The initial selectors use the exact host and anchored operation paths. An extra path component, a sibling operation or a suffix host lookalike does not silently become a web event. An empty final app component remains recognizable so the pack can report the missing identifier. Registration and app ownership need dashboard or receiver evidence beyond the characters visible in the path.

These are HTTP-only payload contracts. A bare JSON object cannot reliably distinguish an event from a visit, particularly when a required discriminator is missing. A bare endpoint URL can supply path and host evidence, but it cannot prove the request method, authentication or body. Selecting a rulepack explicitly does not manufacture those missing observations.

Identity is checked inside the top-level user_id object.

Both packs require the native user_id object and at least one supported identifier inside it. The caller can provide customer_user_id, appsflyer_id or both. Each supplied identifier keeps its own native string and length checks. An empty object has no supported identity, while a numeric identifier is not made valid merely by converting it to a display string.

The customer identifier accepts 1 to 64 characters. The AppsFlyer identifier needs at least one character, with no published upper limit in the endpoint table. Length checks operate on characters rather than equating UTF-8 bytes with characters. Optional fields remain optional, and the pack does not invent a UUID format or trim an opaque identity into a different value.

The endpoint tables and examples put user_id at the root. Another integration paragraph describes a different nested wrapper. The packs follow the explicit endpoint tables and record the source inconsistency. They do not silently accept a nested alias that changes what the contracted request actually contains. A clear scope decision is more reviewable than claiming that the contradictory descriptions have been resolved.

Events and visits keep their different required fields.

A web event needs event_name. Its string length is 1 to 64 characters, and the documented forbidden characters are @, =, + and -. A name that contains one of those characters fails the event contract. The pack retains allowed spaces and Unicode rather than borrowing a stricter name policy from another destination.

A visit needs event_url. That URL is optional on an event, but a supplied event URL still receives its format and length checks. The local rules require an HTTP or HTTPS URL and at most 4,096 characters. An absolute FTP URL cannot satisfy the HTTP contract merely because a general URL parser accepts it.

Web event_value is an optional object, including a supported custom_parameters object. It does not inherit the mobile eventValue string requirement. Custom parameter names and values remain open. The event-specific revenue fields receive their documented native types and currency checks without an invented purchase-only condition or a requirement to include an absent optional amount.

Optional fields receive local representation checks.

Both packs inspect the five supported hashed identity fields: email_hashed, phone_number_hashed, phone_number_e164_hashed, first_name_hashed and last_name_hashed. A supplied hash must have exactly 64 hexadecimal characters. Uppercase and lowercase hex letters are accepted because the source does not impose lowercase encoding. A populated digest with the wrong alphabet or length remains a concrete representation error.

The optional timestamp uses Unix milliseconds and signed 64-bit integer bounds. IP fields accept valid IPv4 or IPv6 addresses rather than hashed digests. User-agent length and the supported deduplication identifiers receive their own documented constraints. Checks preserve native JSON types so a string containing digits does not silently become a numeric timestamp.

Unknown top-level fields remain open, as do custom parameter values. The visit pack does not manufacture an event-name rule for an otherwise unconstrained field merely because a similarly named member has meaning on the event route. The inventory explains which named fields are evaluated, making it possible to inspect remaining gaps without implying a closed schema.

Available transport evidence remains distinct from missing evidence.

The published web operations use HTTPS, application/json and an Authorization header with a populated Bearer token. The packs check those observable requirements through the shared HTTP adapter. Legal media-type case and charset forms retain their normalized essence, while a different observed media type remains a different representation. Token syntax is observable; token ownership and validity are external state.

A complete capture that proves an absent required header can receive a missing-header error. A redacted or unavailable header cannot prove that omission and retains the existing evidence deferral. The same distinction applies to bodies. Method or path findings can still run when body-dependent checks lack enough evidence, so one unavailable field does not erase other concrete request defects.

Raw JSON, explicit binary captures and already supported bounded gzip decoding use the established capture path. This release adds no compression policy or guessed HAR request extension. Unsupported encodings, unknown representation metadata and incomplete captures keep their existing diagnostic boundaries. The website request mode continues to keep submitted captures out of query sharing and usage samples.

The website also fixes two diagnostic links exposed by the browser checks. HTTP capture deferrals now link to their actual core rule anchors, and the directory coverage advisory links to the pack catalog with an explanation of its scope. These links explain why a request was left partially unvalidated. The fix does not change a finding severity or turn incomplete evidence into a vendor defect.

Independent controls cover the table and its scope boundaries.

The authored suite starts with minimal valid event and visit captures, then varies one documented requirement at a time. Identity alternatives, required fields, native types, character boundaries, URL schemes and hash alphabets receive explicit controls. Exact maxima and one-over cases prevent a plausible example from substituting for the full constraint. Unicode fixtures distinguish character lengths from encoded byte lengths.

Routing controls cover wrong hosts, sibling paths, extra segments, bare JSON and the retained mobile operation. Capture controls include unavailable and redacted evidence alongside complete HTTP requests. Open-field fixtures make sure the pack does not reject arbitrary custom data. Expected findings are written from the reviewed source contracts before candidate runtime measurement, preserving an independent standard for the implementation.

The source-authored default-routing suite contains 486 controls. Pixellint 0.42.0 matched 178 expected outcomes and the candidate matches all 486, giving 308 improved outcomes. Native, Node WASM and browser-target WASM agree in all 1,458 complete-report comparisons. Eight malformed-root expectations were corrected during prototype review because absent child fields inside an invalid root do not create extra required-field findings; the source review records that correction. The full workspace passes 591 Rust tests, clippy and formatting, plus the npm smoke suite. Another 708 complete native and WASM comparisons cover 236 representative corpus artifacts. The independent parent review adds 26 separate controls, 52 complete native and WASM comparisons, and checks the field inventory against both official endpoint tables. Actual Chromium also passes 46 desktop and mobile checks covering request results, explicit selection, pack pages, source links, privacy and layout.

The corpus comparison and external limits stay explicit.

No observed AppsFlyer web failures have been established in the current private D1 snapshot. The endpoint gap is supported by the newly published web contract and source-authored requests, rather than a claim that the historical corpus contains rejected web events. The corpus comparison remains useful for checking that existing mobile, analytics and tracking behavior is retained.

The complete frozen corpus replay compares Pixellint 0.42.0 with 0.43.0 across 8,836 stored artifacts using their original capture clocks. Every complete report is unchanged: 324 errors and 769 warnings, with 250 artifacts containing an error and no engine exceptions. The direct URL and JSON capture inventory contains no matching AppsFlyer web endpoint, so this release makes no historical web detection-gain claim. The synthetic controls measure the new endpoint behavior separately. Customer payloads and credentials remain in private local replay files; published examples are synthetic.

A clean local capture cannot establish a previous visit, identity continuity, cookie provenance, successful attribution or cross-network deduplication history. The app-timezone late-arrival deadline requires receipt time and configuration. Hash representation cannot prove the normalization of its hidden preimage, and a syntactically valid Bearer token cannot prove permission. These residual requirements remain in the audit beside the observable contracts.

Sources

Contract pages

The dated argument is above. These pages are the field lists.

Inspect an AppsFlyer web request Docs