Files
OSGAccountServer/docs/PRODUCT_ANALYTICS.md
T
Rocky edd0d9feca
CI / verify (push) Has been cancelled
CI / publish (push) Has been cancelled
Correct product analytics cohorts and reporting
2026-08-21 17:50:35 +08:00

231 lines
7.4 KiB
Markdown

# Product analytics
OSGKeyboard analytics records product behavior metadata only. It is separate
from billing, provider execution and immutable credit ledgers.
## Privacy boundary
Never send or persist:
- audio or audio-derived content;
- raw keyboard input, keystrokes, prompts, context, transcripts or model output;
- Apple subjects, email addresses, names, tokens, API keys or provider
credentials;
- arbitrary property names or free-form text.
The service stores a SHA-256 digest of the random installation identifier, not
the identifier supplied by the client. Event DTO string representations are
redacted. Product analytics rows linked to an account are deleted with that
account. Anonymous installations that never link to an account are retained for
90 days and are eligible for scheduled deletion in a later operational job.
Per-installation keyboard usage summaries are retained for at most 90 days
regardless of account linkage.
## Ingestion API
`POST /v1/analytics/events` accepts one batch of 1 to 50 events.
- Authentication is optional so first-open and pre-login events can be
measured. Invalid bearer credentials are rejected.
- `installationId` must be a client-generated UUID stored in the containing app
and shared with the keyboard extension through the App Group. New clients send
it once at the batch root. During the migration window, the server also accepts
released clients that repeat one identical `installationId` on every event;
missing, incomplete, or conflicting identities reject the entire batch.
- When a valid account session is present, the installation is linked to that
account. An installation cannot later be linked to a different account.
- Every `clientEventId` is a client-generated UUID. The pair
`(installation, clientEventId)` is unique.
- Replaying an identical event is accepted and reported as replayed.
- Reusing an event ID with different values returns `409 conflict`.
- The whole batch is validated before persistence and committed atomically.
- `occurredAt` must be no more than 35 days old and no more than five minutes in
the future. UTC ISO-8601 timestamps are required.
The successful response reports `accepted` and `replayed` event counts. It does
not return account or installation identifiers.
## Keyboard usage summaries
`POST /v1/analytics/keyboard-usage` accepts 1 to 50 finalized UTC-day
summaries. Authentication, installation hashing, account linking, atomic
batching and replay semantics match the event ingestion API.
- Language classification and counting happen on-device.
- Only text manually committed by OSGKeyboard is counted. Voice transcripts,
pasted text, AI-generated output and text entered with another keyboard are
excluded.
- Chinese counts Unicode characters classified with the Han script. English
counts Latin letters. Digits, punctuation and Emoji are counted as `other`.
- Pinyin composition keystrokes are not counted as English; only the final
committed text is classified.
- An input session is one keyboard activation containing at least one
committed character.
- Chinese-only sessions contain Chinese but no English; English-only sessions
contain English but no Chinese; mixed sessions contain both. Other
characters may occur in any of those sessions. Other-only sessions contain
neither Chinese nor English.
- The four language-session counters must sum to `inputSessionCount`, and the
total committed character count must be at least the session count.
- Each installation may submit only one immutable summary per UTC date.
- Accepted dates are from 35 days ago through yesterday. Current-day partial
summaries are rejected.
- Raw text, per-keystroke events, surrounding context, host application
identifiers and free-form properties are never accepted.
The client should keep counters and the finalized outbox in the shared App
Group so either the containing app or keyboard extension can deliver them.
Retries must reuse the stored `clientSummaryId`.
## Event catalog
### Lifecycle events
`FIRST_OPEN`
- Required: `surface=APP`, `acquisitionChannel`.
- Optional: `appVersion`, `osVersion`.
- Must be emitted once per installation. Server idempotency protects retries.
`SESSION_STARTED`
- Required: `surface` (`APP` or `KEYBOARD`).
- Optional: `appVersion`, `osVersion`.
`KEYBOARD_ACTIVATED`
- Required: `surface=KEYBOARD`.
- Emit when the custom keyboard becomes active, not for every keystroke.
### AI events
`AI_FEATURE_STARTED`
- Required: `feature`, `executionMode`, `surface`.
- Optional: `durationBucket` is not allowed.
`AI_FEATURE_SUCCEEDED`
- Required: `feature`, `executionMode`, `surface`, `durationBucket`.
- This is the client-side core value event.
- Managed totals remain server-authoritative; the event provides feature and
surface detail.
`AI_FEATURE_FAILED`
- Required: `feature`, `executionMode`, `surface`, `failureCategory`.
- Optional: `durationBucket`.
- Never include provider response bodies or user content.
### Purchase events
`PURCHASE_VIEWED`
- Required: `surface=APP`.
`PURCHASE_STARTED`
- Required: `surface=APP`.
`PURCHASE_CANCELLED`
- Required: `surface=APP`, `failureCategory=CANCELLED`.
Successful purchases are derived from verified StoreKit transactions and must
not be duplicated as client success events.
### Referral events
`REFERRAL_SHARED`
- Required: `surface=APP`.
`INVITE_OPENED`
- Required: `surface=INVITE_WEB`, `acquisitionChannel=REFERRAL`.
- The app may emit this event after handling an invitation Universal Link.
- The first-party web route separately increments a UTC daily aggregate after
validating the referral code. It stores no IP address, user agent, cookie,
installation ID, referral code, or other request metadata.
- No third-party analytics script is permitted.
Referral binding, value qualification and reward are derived from server data.
## Allowed dimensions
`surface`
- `APP`
- `KEYBOARD`
- `INVITE_WEB`
`acquisitionChannel`
- `APP_STORE_ORGANIC`
- `REFERRAL`
- `SOCIAL_CONTENT`
- `UNKNOWN`
`feature`
- `TRANSCRIPTION`
- `POLISH`
- `AI_ASSISTANT`
- `AGENT`
- `HOTWORD`
- `OTHER`
`executionMode`
- `MANAGED`
- `LOCAL`
- `BYOK`
`failureCategory`
- `NETWORK`
- `PROVIDER`
- `TIMEOUT`
- `CANCELLED`
- `INSUFFICIENT_CREDITS`
- `VALIDATION`
- `UNKNOWN`
`durationBucket`
- `LT_1S`
- `S1_TO_3`
- `S3_TO_10`
- `S10_TO_30`
- `GTE_30S`
`appVersion` and `osVersion` are optional ASCII release identifiers of at most
32 characters. They may not contain spaces, user-generated values or device
names.
## Client delivery guidance
- Persist pending events in an append-only local queue.
- Retry with exponential backoff after network failures and HTTP 5xx.
- Drop events rejected with HTTP 400/422 after recording a local diagnostic
counter; do not retry malformed events indefinitely.
- Reuse the same `clientEventId` for every retry.
- Send at most 50 events per batch and remove events only after a successful
response.
- Do not block the AI interaction or purchase flow on analytics delivery.
## Reporting
The admin analytics endpoints combine:
- accepted product events for acquisition, local/BYOK usage and client funnel
detail;
- settled credit usage for managed AI counts and credit consumption;
- accounts, StoreKit and referrals for registration, monetization and referral
outcomes.
- daily keyboard summaries for privacy-minimized Chinese and English input
activity, character volume and input sessions.
Metric formulas are defined in
[`ANALYTICS_METRICS_DICTIONARY.md`](ANALYTICS_METRICS_DICTIONARY.md).