{"openapi":"3.1.0","info":{"title":"UU App Analytics API","version":"0.1.0","description":"Standalone multi-application analytics API for app telemetry ingestion, app-scoped API clients, admin stats, and recent event export."},"servers":[{"url":"https://analytics.spsw.dev","description":"Production"},{"url":"http://127.0.0.1:4325","description":"Local development"}],"tags":[{"name":"Health"},{"name":"Admin Apps"},{"name":"API Clients"},{"name":"Analytics Ingestion"},{"name":"Stats"}],"paths":{"/health":{"get":{"tags":["Health"],"summary":"Health check","operationId":"getHealth","responses":{"200":{"description":"Service is healthy.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","example":true},"service":{"type":"string","example":"analytics-server"}}}}}}}}},"/api/apps":{"get":{"tags":["Admin Apps"],"summary":"List visible apps","security":[{"adminAuth":[]}],"responses":{"200":{"description":"Visible apps.","content":{"application/json":{"schema":{"type":"object","properties":{"apps":{"type":"array","items":{"$ref":"#/components/schemas/App"}}}}}}},"401":{"description":"Request failed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"post":{"tags":["Admin Apps"],"summary":"Create an app","security":[{"adminAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateAppRequest"}}}},"responses":{"201":{"description":"Created app.","content":{"application/json":{"schema":{"type":"object","properties":{"app":{"$ref":"#/components/schemas/App"}}}}}},"400":{"description":"Request failed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Request failed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/apps/{id}/api-clients":{"get":{"tags":["API Clients"],"summary":"List API clients for an app","security":[{"adminAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"App ID","schema":{"type":"string"}}],"responses":{"200":{"description":"API clients.","content":{"application/json":{"schema":{"type":"object","properties":{"apiClients":{"type":"array","items":{"$ref":"#/components/schemas/ApiClient"}}}}}}},"401":{"description":"Request failed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"post":{"tags":["API Clients"],"summary":"Create an app-scoped analytics API token","description":"Returns the token once. Store it immediately.","security":[{"adminAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"App ID","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateApiClientRequest"}}}},"responses":{"201":{"description":"Created API client and one-time token.","content":{"application/json":{"schema":{"type":"object","properties":{"apiClient":{"$ref":"#/components/schemas/ApiClient"},"token":{"type":"string"}}}}}},"400":{"description":"Request failed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Request failed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/apps/{id}/stats":{"get":{"tags":["Stats"],"summary":"Get aggregate stats for an app","security":[{"adminAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"App ID","schema":{"type":"string"}}],"responses":{"200":{"description":"App stats.","content":{"application/json":{"schema":{"type":"object","properties":{"stats":{"$ref":"#/components/schemas/AppStats"}}}}}},"401":{"description":"Request failed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/apps/{id}/events":{"get":{"tags":["Stats"],"summary":"Export recent normalized events for an app","security":[{"adminAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"App ID","schema":{"type":"string"}},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":1000,"default":100}}],"responses":{"200":{"description":"Recent events.","content":{"application/json":{"schema":{"type":"object","properties":{"events":{"type":"array","items":{"$ref":"#/components/schemas/AnalyticsEvent"}}}}}}},"401":{"description":"Request failed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/analytics/app":{"post":{"tags":["Analytics Ingestion"],"summary":"Record WNBA-style app analytics","description":"Requires an app token with `analytics:write`. Raw unique IDs are hashed before storage.","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AppAnalyticsRequest"}}}},"responses":{"201":{"description":"Recorded analytics event.","content":{"application/json":{"schema":{"type":"object","properties":{"event":{"$ref":"#/components/schemas/AnalyticsEvent"}}}}}},"400":{"description":"Request failed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Request failed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Request failed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/analytics/events":{"post":{"tags":["Analytics Ingestion"],"summary":"Record a generic analytics event","description":"Requires an app token with `analytics:write`.","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenericAnalyticsEventRequest"}}}},"responses":{"201":{"description":"Recorded analytics event.","content":{"application/json":{"schema":{"type":"object","properties":{"event":{"$ref":"#/components/schemas/AnalyticsEvent"}}}}}},"400":{"description":"Request failed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Request failed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Request failed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"App API token with `analytics:write` for ingestion."},"adminAuth":{"type":"http","scheme":"bearer","description":"Admin bearer token. HTTP Basic admin auth is also supported operationally."}},"schemas":{"ErrorResponse":{"type":"object","properties":{"error":{"type":"string"}}},"App":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"bundleId":{"type":"string"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"CreateAppRequest":{"type":"object","required":["name","bundleId"],"properties":{"name":{"type":"string","example":"Flame Games"},"bundleId":{"type":"string","example":"com.silverpine.flame"}}},"ApiClient":{"type":"object","properties":{"id":{"type":"string"},"appId":{"type":"string"},"name":{"type":"string"},"scopes":{"type":"array","items":{"type":"string"},"example":["analytics:write"]},"tokenPreview":{"type":"string"},"createdAt":{"type":"string","format":"date-time"},"revokedAt":{"type":["string","null"],"format":"date-time"}}},"CreateApiClientRequest":{"type":"object","properties":{"name":{"type":"string","example":"iOS production app"},"scopes":{"type":"array","items":{"type":"string"},"example":["analytics:write"]}}},"AppAnalyticsRequest":{"type":"object","properties":{"appId":{"type":"string","description":"Analytics app ID.","example":"app_123"},"bundleId":{"type":"string","description":"Application bundle/package ID.","example":"com.example.app"},"uniqueId":{"type":"string","description":"Stable install or device ID. Stored as a hash."},"osVersion":{"type":"string","example":"iOS 18.5"},"appVersion":{"type":"string","example":"1.2.3"},"buildNumber":{"type":"string","example":"456"},"date":{"type":"string","format":"date-time"},"deviceType":{"type":"string","example":"iPhone17,1"}}},"GenericAnalyticsEventRequest":{"type":"object","required":["eventType"],"properties":{"appId":{"type":"string","description":"Analytics app ID.","example":"app_123"},"bundleId":{"type":"string","description":"Application bundle/package ID.","example":"com.example.app"},"eventType":{"type":"string","example":"screen.view"},"uniqueId":{"type":"string","description":"Stable install or device ID. Stored as a hash."},"timestamp":{"type":"string","format":"date-time"},"osVersion":{"type":"string"},"appVersion":{"type":"string"},"buildNumber":{"type":"string"},"deviceType":{"type":"string"},"properties":{"type":"object","additionalProperties":true}}},"AnalyticsEvent":{"type":"object","properties":{"id":{"type":"string"},"appId":{"type":"string"},"eventType":{"type":"string"},"uniqueIdHash":{"type":["string","null"]},"osVersion":{"type":["string","null"]},"appVersion":{"type":["string","null"]},"buildNumber":{"type":["string","null"]},"deviceType":{"type":["string","null"]},"deviceName":{"type":["string","null"]},"deviceFamily":{"type":["string","null"]},"geoLabel":{"type":["string","null"]},"propertiesJson":{"type":"string"},"createdAt":{"type":"string","format":"date-time"}}},"AppStats":{"type":"object","properties":{"app":{"$ref":"#/components/schemas/App"},"totalEvents":{"type":"integer"},"uniqueDevices":{"type":"integer"},"byDay":{"type":"array","items":{"type":"object","properties":{"date":{"type":"string","example":"2026-06-16"},"total":{"type":"integer"},"uniqueDevices":{"type":"integer"}}}},"byEventType":{"type":"array","items":{"type":"object","properties":{"value":{"type":"string"},"total":{"type":"integer"}}}},"byDeviceType":{"type":"array","items":{"type":"object","properties":{"value":{"type":"string"},"deviceType":{"type":"string"},"deviceName":{"type":["string","null"]},"deviceFamily":{"type":["string","null"]},"total":{"type":"integer"}}}},"byOsVersion":{"type":"array","items":{"type":"object","properties":{"value":{"type":"string"},"total":{"type":"integer"}}}},"byAppVersion":{"type":"array","items":{"type":"object","properties":{"value":{"type":"string"},"total":{"type":"integer"}}}},"byBuildNumber":{"type":"array","items":{"type":"object","properties":{"value":{"type":"string"},"total":{"type":"integer"}}}},"byLocation":{"type":"array","items":{"type":"object","properties":{"value":{"type":"string"},"total":{"type":"integer"}}}},"recentEvents":{"type":"array","items":{"$ref":"#/components/schemas/AnalyticsEvent"}}}}}}}