pixellint

AppsFlyer web events validate their captured endpoint contracts.

The web event operation belongs to events.appsflyer.com/v2.0/s2s/inapps/app/web/{appId}. Its payload rules require captured HTTP endpoint context. Bare JSON cannot establish this operation, and the mobile S2S body remains a separate contract.

A top-level user_id object must contain customer_user_id, appsflyer_id or both. event_name is 1 to 64 characters and forbids @, =, + and -. event_url is optional when sending an event. Unknown fields and custom parameters remain open.

Observed requests require POST, application/json and a populated Authorization Bearer token. Unavailable or redacted evidence is deferred. Live token/app ownership, late-arrival configuration, prior visits and hash preimages remain external.

The explicit endpoint tables require top-level user_id. A separate integration paragraph describes a conflicting nested wrapper. These checks follow the endpoint tables, document that conflict, and do not silently accept a nested alias or claim complete receiver acceptance.

At least one supported identity is required.

An empty user_id object has no supported identity. Either customer_user_id or appsflyer_id is sufficient; supplied values retain native string and length checks. The pack does not infer a UUID grammar or a prior identified session.

Rule: vendor.appsflyer-web-event.http.user_id.identifier_required

A hash must contain exactly 64 hexadecimal characters.

The five optional hashed identity fields accept both uppercase and lowercase hex letters. A digest does not establish correct preimage normalization. Empty or nonhex supplied hashes fail their own local representation checks.

Rule: vendor.appsflyer-web-event.http.email_hashed.invalid

Web event values keep their native object representation.

event_value is optional and must be an object when supplied. It does not inherit the mobile eventValue string contract. Custom parameter names and values stay open.

Rule: vendor.appsflyer-web-event.http.event_value.invalid

An event name follows the web character contract.

event_name is required, has 1 to 64 characters, and cannot contain @, =, + or -. Spaces and Unicode names remain valid when they satisfy that contract. A missing name is still checked because endpoint selection does not depend on a populated event_name.

Rule: vendor.appsflyer-web-event.http.event_name.invalid

A nested user wrapper does not supply the required root identity.

The published endpoint table uses a top-level user_id object. A nested user.user_id value does not satisfy that field. The source inconsistency remains in the audit; unknown fields and custom parameters stay open.

Rule: vendor.appsflyer-web-event.http.user_id.missing

What this pack matches

Hosts
events.appsflyer.com
Paths
Full path expression: ^/v2\.0/s2s/inapps/app/web/[^/]*$
Vendor docs
support.appsflyer.com/hc/en-us/articles/45670116601617-Server-to-Server-S2S-API-for-Web-Performance-Measurement

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
app_id required Required dashboard unified_app_id. Website prefixes and registration truth are not inferred from examples. vendor.appsflyer-web-event.param.app_id.missing
vendor.appsflyer-web-event.param.app_id.empty
docs
method required The published operation uses POST. Complete HTTP capture contract. Native JSON type: string. vendor.appsflyer-web-event.http.method.missing
vendor.appsflyer-web-event.http.method.empty
vendor.appsflyer-web-event.http.method.invalid
docs
content_type required All requests require application/json. Complete HTTP capture contract. Native JSON type: string. vendor.appsflyer-web-event.http.content_type.missing
vendor.appsflyer-web-event.http.content_type.empty
vendor.appsflyer-web-event.http.content_type.invalid
docs
body_encoding required This operation carries a JSON entity. A locally unsupported decoder representation remains unvalidated. Complete HTTP capture contract. Applies when {"kind":"not","condition":{"kind":"value_in","param":"body_encoding","values":["unsupported"]}}. Native JSON type: string. vendor.appsflyer-web-event.http.body_encoding.missing
vendor.appsflyer-web-event.http.body_encoding.empty
vendor.appsflyer-web-event.http.body_encoding.invalid
docs
headers.authorization required The published API requires an Authorization Bearer S2S token. Token validity and ownership remain external. Complete HTTP capture contract. Native JSON type: string. vendor.appsflyer-web-event.http.headers.authorization.missing
vendor.appsflyer-web-event.http.headers.authorization.empty
vendor.appsflyer-web-event.http.headers.authorization.invalid
docs
url required The published endpoint uses HTTPS. Complete HTTP capture contract. Native JSON type: string. vendor.appsflyer-web-event.http.url.missing
vendor.appsflyer-web-event.http.url.empty
vendor.appsflyer-web-event.http.url.invalid
docs
body required The endpoint body is a JSON object. Complete HTTP capture contract. Native JSON type: object. vendor.appsflyer-web-event.http.body.missing
vendor.appsflyer-web-event.http.body.invalid
docs
user_id required The published web table requires a top-level user_id object with at least one documented identifier. Complete HTTP capture contract. Capture scope: body. Native JSON type: object. vendor.appsflyer-web-event.http.user_id.missing
vendor.appsflyer-web-event.http.user_id.invalid
docs
event_url optional The documented page URL uses HTTP or HTTPS and is at most 4096 characters. Complete HTTP capture contract. Capture scope: body. Presence diagnostic severity: error. Native JSON type: string. Maximum decoded characters: 4096. vendor.appsflyer-web-event.http.event_url.empty
vendor.appsflyer-web-event.http.event_url.invalid
docs
event_value optional Optional free-form event parameters remain an open object. Complete HTTP capture contract. Capture scope: body. Native JSON type: object. vendor.appsflyer-web-event.http.event_value.invalid docs
event_value.custom_parameters optional The documented custom_parameters representation is a sub-object. Complete HTTP capture contract. Capture scope: body. Native JSON type: object. vendor.appsflyer-web-event.http.event_value.custom_parameters.invalid docs
timestamp optional Optional Unix milliseconds use the published int64 type. App timezone and receipt time are external. Complete HTTP capture contract. Capture scope: body. Native JSON type: integer. Minimum numeric value: -9223372036854776000. Maximum numeric value: 9223372036854776000. vendor.appsflyer-web-event.http.timestamp.invalid docs
ip optional Optional device IP accepts IPv4 or IPv6, up to 46 characters. Complete HTTP capture contract. Capture scope: body. Presence diagnostic severity: error. Native JSON type: string. Maximum decoded characters: 46. vendor.appsflyer-web-event.http.ip.empty
vendor.appsflyer-web-event.http.ip.invalid
docs
user_agent optional Optional browser user agent is at most 1024 characters. Complete HTTP capture contract. Capture scope: body. Native JSON type: string. Maximum decoded characters: 1024. vendor.appsflyer-web-event.http.user_agent.invalid docs
http_referrer optional The optional referrer is documented as a string. No extra length or scheme restriction is invented. Complete HTTP capture contract. Capture scope: body. Native JSON type: string. vendor.appsflyer-web-event.http.http_referrer.invalid docs
email_hashed optional The optional SHA256 representation must be exactly 64 hexadecimal characters. The preimage cannot be verified locally. Complete HTTP capture contract. Capture scope: body. Native JSON type: string. Minimum decoded characters: 64. Maximum decoded characters: 64. vendor.appsflyer-web-event.http.email_hashed.empty
vendor.appsflyer-web-event.http.email_hashed.invalid
docs
phone_number_hashed optional The optional SHA256 representation must be exactly 64 hexadecimal characters. The preimage cannot be verified locally. Complete HTTP capture contract. Capture scope: body. Native JSON type: string. Minimum decoded characters: 64. Maximum decoded characters: 64. vendor.appsflyer-web-event.http.phone_number_hashed.empty
vendor.appsflyer-web-event.http.phone_number_hashed.invalid
docs
phone_number_e164_hashed optional The optional SHA256 representation must be exactly 64 hexadecimal characters. The preimage cannot be verified locally. Complete HTTP capture contract. Capture scope: body. Native JSON type: string. Minimum decoded characters: 64. Maximum decoded characters: 64. vendor.appsflyer-web-event.http.phone_number_e164_hashed.empty
vendor.appsflyer-web-event.http.phone_number_e164_hashed.invalid
docs
first_name_hashed optional The optional SHA256 representation must be exactly 64 hexadecimal characters. The preimage cannot be verified locally. Complete HTTP capture contract. Capture scope: body. Native JSON type: string. Minimum decoded characters: 64. Maximum decoded characters: 64. vendor.appsflyer-web-event.http.first_name_hashed.empty
vendor.appsflyer-web-event.http.first_name_hashed.invalid
docs
last_name_hashed optional The optional SHA256 representation must be exactly 64 hexadecimal characters. The preimage cannot be verified locally. Complete HTTP capture contract. Capture scope: body. Native JSON type: string. Minimum decoded characters: 64. Maximum decoded characters: 64. vendor.appsflyer-web-event.http.last_name_hashed.empty
vendor.appsflyer-web-event.http.last_name_hashed.invalid
docs
event_name required The web event name is 1 to 64 characters and cannot contain @, =, + or -. Spaces and leading quotation marks are not prohibited by this table. Complete HTTP capture contract. Capture scope: body. Native JSON type: string. Minimum decoded characters: 1. Maximum decoded characters: 64. vendor.appsflyer-web-event.http.event_name.missing
vendor.appsflyer-web-event.http.event_name.empty
vendor.appsflyer-web-event.http.event_name.invalid
docs
event_revenue optional The optional revenue amount is a JSON number. No unpublished nonnegative or dependency condition is added. Complete HTTP capture contract. Capture scope: body. Native JSON type: number. vendor.appsflyer-web-event.http.event_revenue.invalid docs
event_revenue_currency optional Optional revenue currency is an ISO 4217 code. Complete HTTP capture contract. Capture scope: body. Presence diagnostic severity: error. Native JSON type: string. vendor.appsflyer-web-event.http.event_revenue_currency.empty
vendor.appsflyer-web-event.http.event_revenue_currency.invalid
docs
af_customer_event_id optional Optional network deduplication ID is at most 256 characters. Matching across external network pixels is outside this single-capture contract. Complete HTTP capture contract. Capture scope: body. Native JSON type: string. Maximum decoded characters: 256. vendor.appsflyer-web-event.http.af_customer_event_id.invalid docs
http.event_url_http required The documented page URL uses HTTP or HTTPS. Complete HTTP capture contract. Capture scope: body. vendor.appsflyer-web-event.http.event_url_http docs
customer_user_id optional Customer identifier is 1 to 64 characters. Complete HTTP capture contract. Capture scope: body.user_id. Native JSON type: string. Minimum decoded characters: 1. Maximum decoded characters: 64. vendor.appsflyer-web-event.http.customer_user_id.invalid docs
appsflyer_id optional AppsFlyer web identifier has at least one character. No upper limit is published. Complete HTTP capture contract. Capture scope: body.user_id. Native JSON type: string. Minimum decoded characters: 1. vendor.appsflyer-web-event.http.appsflyer_id.invalid docs
http.user_id.identifier_required required Provide at least one identifier in the top-level user_id object. Complete HTTP capture contract. Capture scope: body.user_id. vendor.appsflyer-web-event.http.user_id.identifier_required docs

Validate a payload

pixellint validate request @request.json --rulepack vendor/appsflyer-web-event

Try this failing payload in the playground. The web event omits user_id.

{"url":"https://events.appsflyer.com/v2.0/s2s/inapps/app/web/website-example.com","method":"POST","headers":{"Content-Type":"application/json","Authorization":"Bearer LOCAL_EXAMPLE_TOKEN"},"body":"{\"event_name\":\"af_purchase\"}"}

cargo install pixellint · npm install pixellint