This document defines the end-to-end process by which mobile telemetry events are created, protected, queued, transmitted, validated, stored, acknowledged, retried, quarantined, or purged.
The process applies to both:
- the consumer mobile application, which submits user telemetry; and
- the vendor mobile application, which submits vendor-member telemetry.
The two applications share the same general intake pattern but use separate authenticated routes and principal types.
| Component |
Responsibility |
| Mobile feature or game module |
Produces a typed telemetry event when an observable action occurs |
| Event factory |
Assigns identifiers, sequence values, timestamps, application context, and consent context |
| Consent gate |
Determines whether the event may be collected or retained |
| Encrypted local queue |
Persists the event before upload using AES-GCM and an Android Keystore-backed key |
| Flush coordinator |
Requests delivery when a threshold, lifecycle event, or connectivity condition is met |
| WorkManager uploader |
Delivers queued events when network constraints permit |
| User intake route |
Accepts events from authenticated user principals |
| Vendor intake route |
Accepts events from authenticated vendor-member principals |
| Batch validator |
Validates batch-level limits and application consistency |
| Event validator |
Validates each event independently |
| Idempotency layer |
Prevents duplicate event storage by event_id |
| Intake persistence layer |
Stores accepted batches, accepted events, and rejection metadata |
| Mobile response handler |
Deletes accepted events, retries temporary failures, and quarantines permanent failures |
flowchart TD
A[Observable user or vendor action] --> B[Create typed telemetry event]
B --> C{Collection allowed?}
C -- No --> D[Do not queue event]
C -- Yes --> E[Assign UUID and client sequence]
E --> F[Serialize event JSON]
F --> G[Encrypt with AES-GCM]
G --> H[Write to local queue]
H --> I{Flush condition met?}
I -- No --> J[Remain queued]
I -- Yes --> K[WorkManager prepares batch]
J --> K
K --> L[Send 1-50 events]
L --> M[Authenticate TelemetryPrincipal]
M --> N{Correct intake route?}
N -- No --> O[Reject request]
N -- Yes --> P[Validate batch]
P --> Q[Validate each event independently]
Q --> R{Duplicate event_id?}
R -- Yes --> S[Acknowledge duplicate without new row]
R -- No --> T{Event valid?}
T -- Yes --> U[Store immutable event]
T -- No --> V[Store rejection metadata only]
S --> W[Return partial batch response]
U --> W
V --> W
W --> X[Mobile processes each result]
X --> Y{Accepted or duplicate?}
Y -- Yes --> Z[Delete event from queue]
Y -- No --> AA{Retryable?}
AA -- Yes --> AB[Schedule exponential-backoff retry]
AA -- No --> AC[Quarantine with error code]
A game, application, venue, offer, campaign, or vendor-management action produces a typed telemetry event.
Examples include:
game.selected
game_session.started
game_objective.completed
campaign.activated
redemption.approved
The application records observable facts. It does not calculate behavioral or psychometric conclusions on-device.
The event factory assigns:
- a client-generated UUID as
event_id;
- the active
application_session_id;
- an optional
game_session_id;
- an optional
group_session_id; and
- a monotonically increasing
client_sequence within the application session.
The event_id remains unchanged during every retry. This makes backend ingestion idempotent.
Before the event is queued, the application checks the effective consent state.
flowchart LR
A[Event produced] --> B{User behavioral event?}
B -- No --> E[Continue]
B -- Yes --> C{analytics_allowed = true?}
C -- No --> D[Discard before queueing]
C -- Yes --> E[Continue]
E --> F[Build event envelope]
Vendor operational events are not treated as user behavioral analytics, but they must still include the applicable policy version and approved telemetry context.
The application must persist the event before attempting transmission.
Processing order:
- Serialize the typed event into JSON.
- Encrypt the serialized event using AES-GCM.
- Use a key stored or generated through Android Keystore.
- Insert the encrypted record into the local telemetry queue.
- Mark the record as pending.
The queue therefore remains durable during:
- application termination;
- temporary loss of connectivity;
- device restart;
- backend unavailability; or
- a failed upload attempt.
¶ 5. Flush and Batch Assembly Flow
A queue flush may be requested when:
- the queue reaches the configured event threshold;
- a game session completes;
- the application enters the background;
- network connectivity is restored;
- WorkManager performs a scheduled delivery attempt; or
- another approved lifecycle trigger occurs.
The uploader selects no more than 50 pending events for one request.
Each batch must:
- contain between 1 and 50 events;
- use a single
application_id;
- identify the batch with
batch_id;
- provide
sent_at; and
- preserve each event's original
event_id and occurred_at values.
flowchart TD
A[Read pending queue] --> B[Decrypt candidate records]
B --> C[Deserialize typed events]
C --> D[Group by application_id]
D --> E[Select up to 50 events]
E --> F[Create batch_id and sent_at]
F --> G[POST to correct intake route]
¶ 6. Route Selection and Authentication
User telemetry is sent only through the user intake route.
POST /api/mobile/v1/telemetry/events
The request must resolve to an authenticated TelemetryPrincipal whose principal type is user.
Vendor telemetry is sent only through the vendor intake route.
POST /api/vendor/mobile/v1/telemetry/events
The request must resolve to an authenticated TelemetryPrincipal whose principal type is vendor_member.
The backend rejects the request or event when:
- a user principal submits through the vendor route;
- a vendor-member principal submits through the user route;
- the event actor type differs from the authenticated principal type;
- the event actor ID differs from the authenticated principal ID; or
- a vendor event's
context.vendor_id differs from the vendor associated with the authenticated membership.
flowchart TD
A[Authenticated request] --> B{Route type}
B -- User route --> C{Principal is user?}
B -- Vendor route --> D{Principal is vendor_member?}
C -- No --> E[Reject request]
D -- No --> E
C -- Yes --> F[Validate actor identity]
D -- Yes --> G[Validate actor and vendor membership]
F --> H[Continue]
G --> H
Batch validation occurs before individual event validation.
The backend verifies:
- The batch contains at least one event.
- The batch contains no more than 50 events.
- Every event uses the same
application_id.
- The application is approved for the route and principal type.
sent_at is not beyond the permitted clock-skew window.
- The request can be parsed into the versioned batch contract.
A batch-level failure prevents event processing because the backend cannot safely interpret or authorize the request as submitted.
After the batch passes structural validation, each event is evaluated independently. One invalid event does not prevent valid events in the same batch from being stored.
flowchart TD
A[Event received] --> B{event_id duplicated inside batch?}
B -- Yes --> Z[Reject duplicate occurrence]
B -- No --> C{schema_version = 1?}
C -- No --> Z
C -- Yes --> D{event_type in Tranche 1 catalog?}
D -- No --> Z
D -- Yes --> E{Principal and actor match?}
E -- No --> Z
E -- Yes --> F{Application allowed?}
F -- No --> Z
F -- Yes --> G{Timestamp valid?}
G -- No --> Z
G -- Yes --> H{Required context present?}
H -- No --> Z
H -- Yes --> I{Vendor context matches membership?}
I -- No --> Z
I -- Yes --> J{Consent permits collection?}
J -- No --> Z
J -- Yes --> K{Payload and context sizes allowed?}
K -- No --> Z
K -- Yes --> L{Prohibited fields absent?}
L -- No --> Z
L -- Yes --> M{Payload matches event schema?}
M -- No --> Z
M -- Yes --> N[Proceed to idempotency check]
For each event:
occurred_at may be no more than five minutes in the future relative to server time;
occurred_at may be no more than 30 days old;
- the timezone offset must be within a valid global range; and
- the client sequence must be non-negative.
The 30-day window supports offline collection without allowing indefinite replay of stale events.
¶ 8.3 Schema and catalog rules
Every event must:
- use
schema_version = 1;
- use an event type included in the Tranche 1 event catalog;
- provide a JSON object as its payload;
- satisfy the registered schema for that event type; and
- omit unknown payload fields.
The backend rejects:
- event payloads larger than 32 KiB; and
context.extra larger than 8 KiB.
These limits prevent the telemetry channel from becoming a substitute for file transfer, message storage, or arbitrary document submission.
General telemetry must not contain:
- names, email addresses, telephone numbers, or other direct contact data;
- passwords, authentication tokens, API keys, or credentials;
- message bodies or chat content;
- raw latitude and longitude values; or
- other fields prohibited by the telemetry contract.
When prohibited content is detected, the event is rejected and the payload is not retained.
User behavioral events require:
analytics_allowed = true
If analytics consent is absent or withdrawn, the event is rejected or never queued, depending on where the consent state changes.
¶ 9. Idempotency and Duplicate Handling
After an event passes validation, the backend checks whether its event_id already exists.
flowchart LR
A[Validated event] --> B{event_id already stored?}
B -- No --> C[Insert one immutable event row]
B -- Yes --> D[Do not insert another row]
C --> E[Return accepted]
D --> F[Return accepted as duplicate]
A repeated event_id is acknowledged so the client can safely remove it from the queue. No second event record is created.
Duplicate acknowledgement is essential because a client may lose the original response after the backend has already committed the event.
The migration creates three primary tables.
Stores batch-level intake metadata, including:
- batch identity;
- authenticated principal context;
- route or application context;
- received timestamp;
- submitted event count;
- accepted event count;
- rejected event count; and
- duplicate event count.
Stores accepted events as immutable intake records.
The record includes:
- event identity;
- event type and schema version;
- occurrence and receipt timestamps;
- actor and application context;
- session identifiers;
- canonical event context;
- consent snapshot;
- validated payload; and
- processing status.
Stores rejection metadata such as:
- batch ID;
- event ID, when recoverable;
- rejection code;
- human-readable reason;
- retryability; and
- rejection timestamp.
Rejected event payloads are not stored.
flowchart TD
A[Validated batch] --> B[Event 1 valid]
A --> C[Event 2 invalid]
A --> D[Event 3 duplicate]
B --> E[Insert mobile_events row]
C --> F[Insert rejection metadata only]
D --> G[No new event row]
E --> H[Update batch counters]
F --> H
G --> H
H --> I[Return combined response]
The backend therefore preserves valid data even when other events in the same upload fail validation.
The backend returns one result per submitted event.
Possible outcomes are:
| Outcome |
Meaning |
Client action |
| Accepted |
Event was newly stored |
Delete from local queue |
| Accepted duplicate |
Event was already stored |
Delete from local queue |
| Rejected, retryable |
Temporary condition may resolve |
Retain and retry later |
| Rejected, permanent |
Event cannot become valid without modification |
Move to quarantine |
The response includes:
batch_id;
- ingestion identifier;
- server time;
- accepted event IDs;
- duplicate indicators; and
- rejected event IDs with error code, message, and retryability.
flowchart TD
A[Batch response received] --> B[Process each submitted event]
B --> C{Accepted?}
C -- Yes --> D[Delete from queue]
C -- No --> E{Duplicate acknowledged?}
E -- Yes --> D
E -- No --> F{Retryable rejection?}
F -- Yes --> G[Increment attempt count]
G --> H[Calculate exponential delay]
H --> I[Set next_attempt_at]
F -- No --> J[Move to quarantine]
J --> K[Store error code and diagnostic metadata]
Events are processed independently so that one failure does not block deletion of successfully acknowledged events.
Retryable failures use exponential backoff capped at six hours.
Example progression:
Attempt 1: short delay
Attempt 2: approximately double the prior delay
Attempt 3: approximately double again
...
Maximum delay: six hours
The precise base interval may be configured, but the retry system must:
- retain the original
event_id;
- avoid creating a replacement event;
- honor WorkManager connectivity constraints;
- stop increasing the delay after the six-hour cap; and
- reset or delete the queue item after successful acknowledgement.
Typical retryable conditions include:
- network interruption;
- request timeout;
- temporary backend unavailability;
- rate limiting; or
- an event timestamp slightly beyond permitted future skew because the device clock is inaccurate.
¶ 14. Permanent Failure and Quarantine Flow
A permanent failure indicates that retrying the unchanged event will not succeed.
Examples include:
- unsupported schema version;
- unknown event type;
- actor or vendor mismatch;
- prohibited telemetry field;
- payload exceeding the size limit;
- unknown payload field;
- missing required event context; or
- absent analytics consent for a user behavioral event.
The client moves the event from the active queue into quarantine and records:
event_id;
event_type;
- error code;
- failure time;
- attempt count; and
- limited diagnostic metadata.
The quarantined event is not repeatedly uploaded.
Quarantine must not expose plaintext telemetry if the local diagnostic record contains event content.
When analytics consent is withdrawn:
- New user behavioral analytics events are no longer queued.
- Pending user analytics events are purged from the local queue.
- A scheduled WorkManager upload must not reintroduce purged events.
- Already accepted backend events follow the applicable retention and deletion policy outside the Tranche 1 transmission flow.
flowchart TD
A[Consent withdrawn] --> B[Update effective consent state]
B --> C[Block new behavioral events]
B --> D[Purge pending analytics queue]
D --> E[Cancel or safely complete pending flush work]
E --> F[Continue only with permitted operational events]
sequenceDiagram
participant Feature as Game or App Feature
participant Recorder as Telemetry Recorder
participant Queue as Encrypted Room Queue
participant Worker as WorkManager Uploader
participant Route as Intake Route
participant Validator as Validation Layer
participant DB as Intake Database
Feature->>Recorder: Record typed event
Recorder->>Recorder: Check consent
Recorder->>Recorder: Assign UUID and client sequence
Recorder->>Queue: Encrypt and persist event
Queue-->>Recorder: Queue write committed
Recorder->>Worker: Request flush when applicable
Worker->>Queue: Read up to 50 pending events
Queue-->>Worker: Return encrypted records
Worker->>Worker: Decrypt and assemble batch
Worker->>Route: POST authenticated batch
Route->>Route: Resolve TelemetryPrincipal
Route->>Validator: Validate batch and events
loop For each event
Validator->>DB: Check event_id
alt New valid event
DB->>DB: Insert immutable event
else Existing event_id
DB->>DB: Do not insert duplicate
else Invalid event
DB->>DB: Store rejection metadata only
end
end
DB-->>Route: Accepted, duplicate, and rejected results
Route-->>Worker: Partial batch response
loop For each result
alt Accepted or duplicate
Worker->>Queue: Delete queue record
else Retryable
Worker->>Queue: Update backoff and next attempt
else Permanent failure
Worker->>Queue: Move record to quarantine
end
end
stateDiagram-v2
[*] --> Created
Created --> Discarded: Consent does not permit collection
Created --> Encrypted: Consent permits collection
Encrypted --> Queued: Local transaction commits
Queued --> Uploading: Flush begins
Uploading --> Accepted: Backend stores event
Uploading --> DuplicateAcknowledged: Backend already has event_id
Uploading --> RetryScheduled: Retryable failure
Uploading --> Quarantined: Permanent failure
Uploading --> Queued: Transport failure before response
RetryScheduled --> Uploading: Backoff expires and connectivity available
Accepted --> DeletedFromQueue
DuplicateAcknowledged --> DeletedFromQueue
Quarantined --> [*]
DeletedFromQueue --> [*]
Queued --> Purged: Consent withdrawn
RetryScheduled --> Purged: Consent withdrawn
Purged --> [*]
Discarded --> [*]
| Acceptance criterion |
Process location |
| Migration creates three intake tables |
Section 10 |
Routes require TelemetryPrincipal |
Section 6 |
| User and vendor routes are isolated |
Section 6 |
| Vendor ID must match membership |
Sections 6 and 8 |
| Batch contains 1–50 events |
Sections 5 and 7 |
Batch uses one application_id |
Sections 5 and 7 |
| Five-minute future skew |
Section 8.2 |
| Thirty-day offline age |
Section 8.2 |
| Schema version 1 required |
Section 8.3 |
| Event must be in Tranche 1 catalog |
Section 8.3 |
| Unknown fields rejected |
Section 8.3 |
| Payload maximum 32 KiB |
Section 8.4 |
context.extra maximum 8 KiB |
Section 8.4 |
| Direct identifiers and sensitive content rejected |
Section 8.5 |
| User behavioral events require analytics consent |
Sections 4.3 and 8.6 |
| Duplicate event IDs do not create another row |
Section 9 |
| Valid events survive partial batch failure |
Section 10.4 |
| Rejected payloads are not retained |
Sections 8.5 and 10.3 |
| Client UUID and monotonic sequence |
Section 4.2 |
| Queue write occurs before upload |
Section 4.4 |
| Queue uses Keystore-backed AES-GCM |
Section 4.4 |
| Upload contains at most 50 events |
Section 5 |
| Accepted events are deleted |
Sections 11 and 12 |
| Retry backoff capped at six hours |
Section 13 |
| Permanent failures are quarantined |
Section 14 |
| Connectivity-constrained WorkManager delivery |
Sections 5 and 13 |
| Flush on completion or backgrounding |
Section 5 |
| Pending analytics purged on consent withdrawal |
Section 15 |
- No telemetry event is lost merely because the device is temporarily offline.
- No event is uploaded before it is durably written to the encrypted local queue.
- No duplicate backend event row is created when the client retries the same
event_id.
- A malformed event does not cause valid sibling events in the same batch to be discarded.
- A user event cannot be submitted as a vendor event, or the reverse.
- A vendor member cannot attribute telemetry to a vendor outside the authenticated membership.
- General telemetry cannot be used to transmit direct contact data, credentials, message bodies, or raw coordinates.
- User behavioral analytics cannot be collected without effective analytics consent.
- Accepted and duplicate events are removed from the device queue.
- Temporary failures remain recoverable without uncontrolled retry frequency.
- Permanent failures stop consuming bandwidth and are retained only as limited diagnostic records.
- Rejected payloads are not persisted by the backend.
This segment ends after an accepted event has been written to analytics_intake.mobile_events and acknowledged to the client.