Mobile crash and vitals ingest (server half)
Watchtower ships native SDKs for iOS (sdks/ios), Android (sdks/android), React Native (sdks/react-native), Flutter/Dart (sdks/dart) and Kotlin Multiplatform (sdks/kmp). They send crashes, ANRs/hangs, app-start timing, slow/frozen frames, RUM batches and masked screenshot replay, with an offline buffer. The endpoints below are what any mobile SDK — ours or a third party's that emits the same shapes — sends to.
Crashes (Sentry envelopes)
Send events to the Sentry-compatible endpoint. Events whose mechanism.type or exception type is an ANR, watchdog termination, out-of-memory, signal or Mach exception are stored as exceptions with tags crash.class (anr, watchdog_termination, oom, signal, mach), crash.handled=false and app.in_foreground. Device, OS and app contexts are kept; the user IP is never stored. Native frames need instruction_addr and debug_id; upload dSYM, ELF or ProGuard files for symbolication.
Sessions and vitals (RUM batch or OTLP)
Events with an os.name of iOS, iPadOS, Android, tvOS or watchOS are stored with device_class=mobile. These vitals are accepted (stored in the RUM events by name):
app_launch_cold,app_launch_warm— launch time in ms. They getwt.rating: cold good at or below 2000, poor above 4000; warm good at or below 1000, poor above 2000. Per-phase timing lives inlaunch.phase.{process,application,activity,ui,first_frame}_msand feeds the App launches view.app_hang— main-thread hang duration in ms.frozen_frames,slow_frames— frame counts.cpu_ticks_per_s,memory_mb— runtime resource usage.
Query them on GET /v1/projects/{prj}/rum/vitals?metric=<name>&device_class=mobile, which returns the same p50/p75/p95 time series as the web vitals. Only launch time is rated; the other mobile metrics have no good/needs-improvement/poor thresholds, so those shares are 0. GET /v1/projects/{prj}/rum/vitals/by_screen breaks a metric down per screen (the view route). Both accept a flag=key:value filter to compare variants.
Each resumed screen is a RUM view; errors, network requests and feature-flag evaluations attach to the session like on web. Masked screenshot replay chunks upload to the replay endpoint.