Source: Decompiled WhatsApp Android version 2.26.3.79. All endpoints, operation names, and URL patterns are as observed in the client binary.
WhatsApp doesn't use a single GraphQL endpoint. It operates a distributed mesh of specialized GraphQL gateways, each serving a distinct domain: payments, generative AI, Oculus/AR, zero-rating, and core messaging. This federated graph architecture enables independent scaling, regional deployment, and security isolation between domains that have no business touching each other's data.
This is a deep-dive into how that mesh is structured, how mutations are routed across it, and what the URL patterns reveal about Meta's broader infrastructure strategy.
The Three-Schema Foundation
Before getting into endpoints, it helps to understand the schema layer they serve:
| Schema | Owner | Purpose |
|---|---|---|
whatsapp |
WhatsApp team | Core messaging, AI features, payments, maps |
whatsapp_mex |
MEX platform team | High-trust: DMA interop, parental controls, passkeys, identity |
facebook |
Meta platform team | Cross-Meta: ads, commerce, digital content |
Every GraphQL operation in the client is tagged to one of these schemas. That tag determines which master endpoint receives the request and which slave services it can fan out to. The schema is the routing key.
1. The GraphQL Endpoint Mesh
Complete Endpoint Inventory
// Core GraphQL endpoints (from decompiled client)
X_GRAPH_FACEBOOK_GRAPHQL_URL // https://graph-www.facebook.com/graphql
X_GRAPH_FACEBOOK_ZERO_RATING_BOOTSTRAP_URL // https://b-graph.facebook.com/graphql
X_GRAPH_OCULUS_GRAPHQL_URL // https://graph.oculus.com/graphql
X_GRAPH_PAYMENTS_GRAPHQL_URL // https://payments-graph.facebook.com/graphql
X_GRAPH_FACEBOOK_GENAI_GRAPHQL_URL // https://genai-graph.facebook.com/graphql
X_GRAPH_INSTAGRAM_GENAI_GRAPHQL_URL // https://genai-graph.instagram.com/graphql_www
X_GRAPH_INSTAGRAM_PAYMENTS_GRAPHQL_URL // https://payments-graph.instagram.com/graphql_www
// WhatsApp-specific endpoints
https://graph.whatsapp.net/v2.2/maps_configs
https://graph.whatsapp.net/wa_qpl_data
https://static.whatsapp.net/wa/static/network_resource
https://static.whatsapp.net/sticker?img=
https://wa.me/stickerpack/%s
https://chat.whatsapp.com/
// Third-party integrations
https://api.giphy.com/v1/stickers/search // Giphy stickers (pg-13 filtered)
https://scontent.oculuscdn.com/ // Oculus/Meta VR content
https://lookaside.facebook.com/assets/key/ // Meta design system assets
Endpoint Purpose Map
| Endpoint | Purpose | Schema |
|---|---|---|
graph-www.facebook.com |
Core Facebook graph | facebook |
b-graph.facebook.com |
Zero-rated bootstrap (emergency access) | Subset of core |
graph.oculus.com |
VR/AR metadata, smart glasses | Oculus schema |
payments-graph.facebook.com |
Payment processing |
facebook with payment scope |
genai-graph.facebook.com |
Generative AI (Imagine, MEmu) | GenAI schema |
genai-graph.instagram.com |
Instagram AI features | Cross-platform AI |
payments-graph.instagram.com |
Instagram payments | Instagram commerce |
graph.whatsapp.net |
Core WhatsApp messaging |
whatsapp / whatsapp_mex
|
This is not a monolith. Each endpoint is owned by a different team, scales independently, has separate authentication requirements, and — critically — separate data access boundaries. The GenAI endpoint has no access to your contact list. The payments endpoint has no access to your message history. That isolation is the point.
Why Federated GraphQL Over a Monolith?
The conventional alternative — a single GraphQL endpoint with a unified schema — breaks down at Meta's scale for several reasons:
Independent deployability. If the GenAI service needs a schema change, it doesn't require a coordinated deploy across the entire messaging infrastructure. Each team ships on their own cadence.
Security isolation. A SQL injection or logic flaw in the Giphy proxy can't escalate to contact list access because those services literally don't share a schema or auth context.
Regional compliance. EU data residency requirements can be enforced at the endpoint level — EU users' MEX operations route to Frankfurt infrastructure, never leaving the region. A monolith makes this significantly harder.
Failure containment. If genai-graph.facebook.com goes down during a viral "imagine me" moment, core messaging at graph.whatsapp.net is unaffected. The master-slave fan-out pattern provides graceful degradation.
2. The Master-Slave Mutation Architecture
Operation Hashing: The Core Pattern
Every GraphQL operation in the WhatsApp client is represented by a triple-hash system rather than inline query text:
{
"AddMEmuProfilePhotos": {
"operation_name": "AddMEmuProfilePhotos",
"operation_name_hash": "793005150",
"operation_text_hash": "6669169098671059140",
"client_doc_id": "7930051506669169098671059140",
"schema": "whatsapp"
}
}
Three hashes, three purposes:
| Hash | Purpose |
|---|---|
operation_name_hash |
Route to correct service / operation registry |
operation_text_hash |
Look up persisted query text on the server |
client_doc_id |
Composite cache/CDN key (name hash + text hash concatenated) |
This is the persisted queries pattern: the full GraphQL query text is never sent over the network. The client sends only the doc_id and variables. The server looks up the query text by hash from a pre-registered store and executes it.
Why this matters:
- Bandwidth savings — A complex mutation with nested fragments might be 2–3KB of query text. Sending a 26-character hash instead adds up across 2.5 billion daily users.
- Security — Arbitrary query execution is impossible. The server will only run pre-registered, pre-validated queries. This eliminates an entire category of GraphQL abuse (deeply nested queries, field enumeration, introspection attacks).
- Build-time validation — Operations are validated against the schema at build time. Type mismatches and missing fields are caught before the APK ships, not at runtime.
- Analytics — Every operation is identified by a stable hash, making performance tracking and error attribution trivial even across client versions.
The Wire Protocol
What the client actually sends:
POST https://graph.whatsapp.net/graphql
{
"doc_id": "7930051506669169098671059140",
"variables": {
"photos": ["base64_photo_1", "base64_photo_2"],
"profile_id": "123456789"
}
}
What the server resolves and executes:
mutation AddMEmuProfilePhotos($photos: [String!]!, $profile_id: ID!) {
addMEmuProfilePhotos(photos: $photos, profile_id: $profile_id) {
success
profile {
id
photo_count
preview_url
}
errors {
code
message
}
}
}
The query text never touches the network. The client binary contains hashes; the server holds the query registry.
Master-Slave Routing
The schema field in each operation manifest determines which GraphQL master handles the request:
schema: "whatsapp" → graph.whatsapp.net
schema: "whatsapp_mex" → Internal MEX gateway (higher trust, separate auth)
schema: "facebook" → graph-www.facebook.com
Each master can then fan out to specialized slave services:
graph.whatsapp.net (whatsapp master)
├── genai-graph.facebook.com (AI image generation)
├── payments-graph.facebook.com (UPI/Pix processing)
└── graph.oculus.com (AR/smart glasses)
MEX gateway (whatsapp_mex master)
├── Compliance services (DMA enforcement, audit logging)
├── PAA services (parental supervision)
└── Monetization services (Wamo/channel subscriptions)
graph-www.facebook.com (facebook master)
└── Ads services (Meta Ads, business tools)
Full Operation Routing Table
| Operation Prefix | Schema | Master Endpoint | Slave Endpoints |
|---|---|---|---|
GenAI*, Imagine*, MEmu*
|
whatsapp |
graph.whatsapp.net |
genai-graph.facebook.com, genai-graph.instagram.com
|
*Payment*, *Upi*, *Pix*
|
whatsapp |
graph.whatsapp.net |
payments-graph.facebook.com |
*Interop*, *Paa*, *Passkey*
|
whatsapp_mex |
Internal MEX gateway | Compliance services |
*Newsletter*, *Wamo*
|
whatsapp_mex |
Internal MEX gateway | Monetization services |
*Ad*, *Commerce*, *DigitalContent*
|
facebook |
graph-www.facebook.com |
Ads services |
*Oculus*, *AR*
|
whatsapp |
graph.whatsapp.net |
graph.oculus.com |
3. Sample Mutations by Domain
A. Generative AI (MEmu / Imagine)
# Create AI avatar profile from user photos
mutation CreateMEmuProfile($photos: [String!]!, $name: String) {
createMEmuProfile(photos: $photos, name: $name) {
profile_id
status
preview_url
estimated_completion_seconds
}
}
# Generate image from text prompt
mutation GenAIImagineGenerateMutation($prompt: String!, $style: ImagineStyle) {
genAIImagineGenerate(prompt: $prompt, style: $style) {
generation_id
image_url
thumbnail_url
seed
prompt_embedding
}
}
# Animate a static image
mutation GenAIEditAnimateMutation($image_id: ID!, $animation_style: AnimationStyle) {
genAIEditAnimate(image_id: $image_id, animation_style: $animation_style) {
video_url
duration_ms
frames
}
}
# Bulk send AI-generated media to a chat
mutation GenAIImagineBulkSendMediaToChat($media_ids: [ID!]!, $chat_jid: String!) {
genAIImagineBulkSendMediaToChat(media_ids: $media_ids, chat_jid: $chat_jid) {
success_count
failed_ids
message_id
}
}
Note prompt_embedding in the Imagine response — the server returns the semantic embedding of the prompt, not just the image. This is likely used for client-side related prompt suggestions and "try similar" features without requiring another round-trip.
B. Payments (UPI / Pix)
# Generate payment key for UPI transaction
mutation GenCreatePaymentKey(
$amount: Int!,
$currency: String!,
$receiver_vpa: String!
) {
genCreatePaymentKey(
amount: $amount,
currency: $currency,
receiver_vpa: $receiver_vpa
) {
payment_key
expiry_seconds
upi_qr_code_url
}
}
# Complete a Pix transaction (Brazil)
mutation CompletePixTransaction($transaction_id: ID!, $pix_key: String!) {
completePixTransaction(
transaction_id: $transaction_id,
pix_key: $pix_key
) {
status
receipt_url
confirmation_code
error_code
}
}
# UPI onboarding OTP
mutation UpiOnboardingSendOtpMutation($phone_number: String!, $bank_code: String) {
upiOnboardingSendOtp(phone_number: $phone_number, bank_code: $bank_code) {
otp_ref_id
resend_timeout_seconds
masked_phone
}
}
The separation of GenCreatePaymentKey (which talks to UPI rails) from CompletePixTransaction (Brazil's Pix system) reflects the reality that WhatsApp Payments is deployed differently by region. India uses UPI with NPCI settlement; Brazil uses Pix with Banco Central do Brasil oversight. These are distinct regulatory environments with distinct backend integrations, and the mutation structure reflects that.
C. MEX Operations (DMA / Parental / Passkeys)
# Create interoperable group with third-party platform users
mutation GroupsCreateInteropGroup(
$name: String!,
$participants: [InteropParticipantInput!]!
) {
groupsCreateInteropGroup(name: $name, participants: $participants) {
group_id
invite_link
external_platform_mappings {
platform
external_id
invite_url
}
}
}
# Child accepts parental supervision
mutation PaaAcceptLinkingMutation($supervisor_id: ID!, $pin: String!) {
paaAcceptLinking(supervisor_id: $supervisor_id, pin: $pin) {
status
supervision_level
activity_sharing_enabled
next_review_date
}
}
# Complete FIDO2 passkey registration
mutation RegistrationPasskeyFinishRegisterMutation(
$challenge: String!,
$attestation: String!
) {
registrationPasskeyFinishRegister(
challenge: $challenge,
attestation: $attestation
) {
success
passkey_id
backup_eligible
}
}
The backup_eligible field in the passkey response is significant — it maps to the FIDO2 concept of a "multi-device credential" (a passkey synced via iCloud Keychain or Google Password Manager) vs. a "single-device credential" (hardware-bound, not synced). WhatsApp is tracking this distinction per passkey, which matters for account recovery: if your only passkey is hardware-bound and you lose the device, there's no backup path.
D. Newsletter / Channel Mutations
# Create a verified channel
mutation NewsletterCreateVerified(
$name: String!,
$description: String,
$verification_docs: [String!]
) {
newsletterCreateVerified(
name: $name,
description: $description,
verification_docs: $verification_docs
) {
newsletter_id
vanity_url
verification_status
admin_ids
}
}
# Enable paid subscription (Wamo)
mutation NewsletterEnableWamoSub(
$newsletter_id: ID!,
$monthly_price: Int!,
$currency: String!
) {
newsletterEnableWamoSub(
newsletter_id: $newsletter_id,
monthly_price: $monthly_price,
currency: $currency
) {
subscription_tier
payout_account_status
subscriber_count
}
}
payout_account_status confirms that creator payouts are a first-class concern in the data model — not a post-hoc integration with an external payments provider, but something WhatsApp tracks directly. Combined with the currency field accepting multiple values, this suggests Wamo payouts will support multiple currencies from launch, relevant for WhatsApp's largest markets (India, Brazil, Nigeria, Indonesia).
4. The QPL Telemetry Pipeline
HWQ hwq = new HWQ(..., "https://graph.whatsapp.net/wa_qpl_data", ...);
hwq.A08("access_token", "1063127757113399|745146ffa34413f9dbb5469f5370b7af");
QPL = Query Performance Logger — Meta's internal distributed tracing system. Every GraphQL operation, database query, and UI render sends performance telemetry to this endpoint.
The hardcoded token is a public app token scoped exclusively to QPL data ingestion:
- App ID
1063127757113399= WhatsApp QPL application - Scope is write-only to
wa_qpl_data— it cannot read user data, contact lists, or message content - Exposing it in the binary is intentional — it's the equivalent of a public write-only API key for a logging endpoint
The QPL Mutation
mutation LogPerformanceTrace(
$trace_id: ID!,
$operation_name: String!,
$duration_ms: Int!,
$cache_hit: Boolean!,
$error_code: Int
) {
logPerformanceTrace(
trace_id: $trace_id,
operation_name: $operation_name,
duration_ms: $duration_ms,
cache_hit: $cache_hit,
error_code: $error_code
) {
accepted
sampling_rate
}
}
Note the sampling_rate in the response — the server tells the client what percentage of subsequent traces to send. This is dynamic sampling: during low-traffic periods, sample 100% of traces; during peak load, drop to 1% to avoid the telemetry pipeline itself becoming a bottleneck. The client adjusts its reporting rate in real time based on server guidance.
This telemetry infrastructure allows Meta engineers to:
- Detect slow GraphQL resolvers (p95 latency by operation name)
- Optimize cache hit rates per operation
- Identify error spikes before they surface in user complaints
- A/B test infrastructure changes with statistical confidence
- Correlate client-side duration with server-side resolver timing
For a 2.5 billion user app, even 1% sampling gives millions of data points per hour. The QPL pipeline is how Meta knows when something is slow before users notice.
5. Third-Party Integrations
Giphy Sticker Search
public String A03(CharSequence charSequence, String str) {
String[] strArr = new String[10];
strArr[0] = "api_key";
strArr[1] = AbstractC11310fq.A0M; // Giphy API key
strArr[2] = "q";
strArr[4] = "lang";
strArr[6] = "rating";
strArr[7] = "pg-13"; // Hardcoded content filter
strArr[8] = "limit";
strArr[9] = "100";
return FIO.A00("https://api.giphy.com/v1/stickers/search", strArr);
}
The pg-13 rating filter is hardcoded, not configurable. This means adult content is filtered at the API call level regardless of user age or settings — a deliberate product decision, not a Giphy default. WhatsApp is a family communication app in most of its core markets; this is a conscious content policy choice baked into the client binary.
The architecture here is a direct proxy: WhatsApp calls Giphy from the client, not through a Meta backend. Giphy data never enters Meta's GraphQL infrastructure. This keeps Giphy content legally and architecturally separate from Meta's data processing — relevant for EU data transfer regulations.
User search query → WhatsApp client → Giphy API directly → Results returned to client
(Meta servers never see Giphy content)
Oculus CDN Asset Structure
https://scontent.oculuscdn.com/v/t64.5771-25/499303769_711556704750162_3777318892782513230_n.mp4
?_nc_cat=105 → CDN category (105 = video asset)
&ccb=1-7 → Cache control bucket
&_nc_sid=5f5f54 → Session ID for analytics
&_nc_ohc=... → Oculus content hash (content-addressed)
&_nc_oc=... → Origin content ID
&_nc_zt=28 → Zero-trust security token
&_nc_ht=scontent.oculuscdn.com
&oh=00_Afn4... → Origin hash (integrity check)
&oe=69457811 → Origin expiration (Unix timestamp)
The _nc_zt (zero-trust token) and oh (origin hash) parameters together implement signed URL access control — the URL is only valid for a specific client session for a limited time window (oe = expiry). Sharing or scraping the URL without a valid session token returns 403. This is standard CDN security for premium content but interesting to see applied to onboarding/tutorial videos.
The content (t64.5771-25 type prefix) is likely a smart glasses onboarding video or AR effect tutorial served to WhatsApp clients supporting Meta's Ray-Ban Stories / Orion integration.
6. Static Asset Architecture
WhatsApp uses purpose-built static domains for different asset types:
// Sticker images (individual)
"https://static.whatsapp.net/sticker?img="
// Sticker packs (bulk)
"https://wa.me/stickerpack/%s"
// Network resources (animations, success states)
"https://static.whatsapp.net/wa/static/network_resource
?cat=nw_media&id=username_success_confetti_tall_green"
// Group invite links
"https://chat.whatsapp.com/"
// Meta design system assets (shared across all Meta apps)
"https://lookaside.facebook.com/assets/key/meta_brand_design_system_icons_raster"
lookaside.facebook.com is Meta's global asset delivery network for design system assets — icons, color tokens, raster graphics — shared across WhatsApp, Instagram, Facebook, and Messenger. When Meta updates an icon in their design system, it propagates to all four apps via a CDN cache invalidation rather than four separate app releases. This enables consistent visual branding across the Meta family without requiring coordinated app updates.
The network_resource endpoint for animations (confetti, success states) is particularly efficient — these are not bundled in the APK but fetched on demand and cached. The APK stays smaller; the CDN handles distribution of infrequently-used animation assets.
7. Map Infrastructure
static {
CCR ccr = new CCR(
"https://www.facebook.com/maps/tile/?", // Tile server
"https://www.facebook.com/maps/static/?", // Static map images
null, null, null, Integer.MAX_VALUE
);
A09 = ccr; // Facebook maps config
A0A = new CCR(
"https://maps.instagram.com/maps/tile/?",
"https://maps.instagram.com/maps/static/?",
null, null, null, Integer.MAX_VALUE
); // Instagram maps config
}
The map configuration is fetched dynamically:
GET https://graph.whatsapp.net/v2.2/maps_configs
?fields=base_url,static_base_url,osm_config,url_override_config
&pretty=0
&access_token=TOKEN
Key architectural insight: the tile server URL is fetched from the server, not hardcoded. The url_override_config field allows Meta to hot-switch the tile provider (from Facebook's servers to a third-party or regional provider) without an app update. This is useful for regional compliance — in markets where Facebook infrastructure is blocked or restricted, the tile server can be redirected to a neutral CDN.
WhatsApp uses OpenStreetMap (osm_config) for map data — community-maintained, license-compatible, and not subject to Google Maps API pricing. Facebook and Instagram serve as tile renderers on top of OSM data, enabling:
- Location sharing with live map previews
- Business location display in profiles
- Event location maps
- Location-based AR effects
8. End-to-End Flow: "Imagine Me"
Putting it all together with a concrete example. When a user sends "imagine me as an astronaut" to Meta AI in WhatsApp:
1. Client hashes operation
CreateMEmuProfile → doc_id: "1941315421..."
2. Client sends to whatsapp master
POST graph.whatsapp.net/graphql
{ doc_id: "1941315421...", variables: { photos: [...], prompt: "astronaut" } }
3. WhatsApp master validates
- Auth token verification
- Rate limit check (per-user, per-operation)
- Persisted query lookup by doc_id
4. Master routes to GenAI slave
POST genai-graph.facebook.com/graphql
(with scoped auth token, no contact list access)
5. GenAI slave processes
- Photo embedding generation
- Diffusion model inference
- Result CDN upload
6. Response propagates back
genai slave → whatsapp master → client
7. QPL telemetry fires
POST graph.whatsapp.net/wa_qpl_data
{ operation: "CreateMEmuProfile", duration_ms: 4200, cache_hit: false }
Total round-trips: 2 (mutation + telemetry). The GenAI slave's data access is cryptographically scoped — it processes photos for the AI task but has no API access to the user's contacts, messages, or other profile data.
9. Security Architecture
Token Scoping (Principle of Least Privilege)
The QPL token demonstrates Meta's token scoping model:
1063127757113399|745146ffa34413f9dbb5469f5370b7af
└── App ID ────┘└── App secret (write-only, QPL scope) ─┘
- Can write to
wa_qpl_dataonly - Cannot read any user data
- Cannot execute GraphQL mutations outside QPL schema
- Public exposure is intentional — it's a write-only logging key
Each slave service receives a scoped token from the master with only the permissions needed for that operation. The GenAI slave token cannot query the payments service. The payments token cannot access the contact graph. The MEX gateway uses a higher-trust token with audit logging enabled.
Zero-Rating Bootstrap
b-graph.facebook.com is a zero-rated endpoint — accessible even when the user has no mobile data balance. Operators in participating markets (primarily in South and Southeast Asia, sub-Saharan Africa) have agreements with Meta to not charge data for this subdomain.
The endpoint exposes only a strict subset of the full API:
- Emergency registration and SIM verification
- Critical security updates and key rotation
- Basic message queuing (not full messaging)
- Account recovery flows
This prevents abuse (zero-rated endpoints are high-value targets for data scraping) while ensuring essential functionality remains accessible to users on prepaid plans. The restricted schema means even if someone discovers the zero-rated endpoint, they can't use it to access arbitrary API functionality for free.
Persisted Queries as a Security Control
The operation hashing system is also a security mechanism. Because the server only executes pre-registered queries:
-
No introspection attacks — you can't query
__schemato enumerate available fields - No batch abuse — you can't craft a custom query that joins 10 expensive resolvers
- No field enumeration — you can't discover undocumented fields by probing the schema
- Audit trail — every executed query is identifiable by a stable hash in logs
This is a meaningful security posture for an app handling 100+ billion messages per day.
Summary
WhatsApp's URL and GraphQL architecture reveals a federated master-slave system built around three core principles:
Security isolation over convenience. Separate endpoints, separate auth tokens, separate data scopes. The GenAI service literally cannot access your contact list, by architecture, not just by policy.
Operational independence. Each domain (AI, payments, messaging, compliance) deploys, scales, and fails independently. A viral "imagine me" moment doesn't degrade core messaging performance.
Regulatory partitioning. The MEX gateway, the zero-rating bootstrap, the scoped token model — all of these are designed for a world where different operations face different regulatory requirements in different jurisdictions. The architecture makes compliance enforceable at the infrastructure level, not just the policy level.
For an app serving 2.5 billion users across radically different regulatory environments — EU DMA, India UPI regulations, Brazil Pix, US compliance requirements — this kind of federated, domain-isolated architecture isn't over-engineering. It's the minimum viable structure for operating at that scale and that regulatory complexity simultaneously.
Analysis based on decompiled GraphQL operation manifests and URL patterns from WhatsApp Android 2.26.3.79.
Top comments (0)