231 lines
7.4 KiB
Markdown
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).
|