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.
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.
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.
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.
What this pack matches
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.missingvendor.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.missingvendor.appsflyer-web-event.http.method.emptyvendor.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.missingvendor.appsflyer-web-event.http.content_type.emptyvendor.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.missingvendor.appsflyer-web-event.http.body_encoding.emptyvendor.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.missingvendor.appsflyer-web-event.http.headers.authorization.emptyvendor.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.missingvendor.appsflyer-web-event.http.url.emptyvendor.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.missingvendor.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.missingvendor.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.emptyvendor.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.emptyvendor.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.emptyvendor.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.emptyvendor.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.emptyvendor.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.emptyvendor.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.emptyvendor.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.missingvendor.appsflyer-web-event.http.event_name.emptyvendor.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.emptyvendor.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