Skip to main content
Analytics is a reading of telemetry Orbit already stores. There is no second SDK, no second key and no second vendor: the sessions, screens and events OrbitCapture sends are what every number here is computed from, under the same capture policy and the same privacy floor.

What is different about it

Every analytics product an iOS team is offered was built for the web and adapted. This one starts from the app.
  • Sessions are lifecycle sessions, not 30 minute timeouts. A session starts when the app comes to the foreground and ends when it goes to the background and stays there. Session length is foreground time only, so a phone left in a pocket does not become engagement.
  • The app version is a first-class dimension, not a custom property. The header carries a version selector and the Versions tab is a table of versions.
  • No IDFA, ever, and no App Tracking Transparency prompt.

Identities: how a person is counted

Set in Settings, then Analytics. It changes what every number means for everyone in the workspace, so writing it is admin or owner. Anonymous mode means no day can be joined to another, so retention answers D0 and D1 only, which the console says out loud. Saving it writes into the capture policy, so the change appears in Settings, Telemetry’s change log with a version of its own and every device picks it up on its next poll.
The policy poll and an upload request still carry the real installation id in their header, even in anonymous mode. That is how the console asks a specific phone for its buffer, and it is not an analytics record.
The other setting that decides what the numbers mean is the activation event: the event that means this person got the point of the app. Every lifecycle state and the time-to-activation number is measured from it. Orbit never picks one for you: it offers the most common event your app sends as a placeholder, and until you choose, nobody is counted as activated.

Lifecycle states

Where everyone stands, one state each, evaluated nightly. The first rule that matches wins, so paying beats activated and churned beats at risk. Every state on the Overview lifecycle bar is a link into People filtered to it.

The tabs

  • Overview: active now (read live, not from a rollup, because a five minute question answered by a nightly job would be about last night), eight usage tiles with sparklines, retention D1, D7 and D30, the lifecycle bar, what changed (the metrics whose move is larger than two standard deviations of their own recent daily variation, rather than a fixed percentage that is loud on a small fleet and silent on a large one), and the top screens and events.
  • Versions: the adoption curve, and a table of every version with sessions, adoption, crash free sessions, hang rate, D1 and D7 retention, revenue per active and store rating, optionally with a delta column against the version below.
  • People: everybody the project has seen, in their lifecycle state, with segments. Pro and up.
  • Behaviour: events, screens, paths, funnels, and rage and dead taps. Pro and up.
  • Revenue: the money tab, below. Pro+ and up.
  • Reports: the digest, saved queries, export and Ask analytics. Pro+ and up.
A day with no value is a gap, not a zero. Null means the rollup has not written that day; zero means nobody opened the app. Collapsing the first into the second is how a chart quietly claims an outage.

Revenue: three sources that see different things

Money is append-only and written by sources that cannot see the same things. Whichever arrives first writes the row; the others append themselves and fill in only what they alone know. A fourth door, POST /v1/ingest/revenue, takes revenue Orbit cannot read from the App Store at all: a web checkout, an invoice, a code redeemed off platform. The three are not meant to match exactly, and the reconciliation card is where a gap that explains itself is told from one that does not. Raw transaction ids never enter a column: only sha256 of them does, and the three sources converge on the same hashes.

Setting up the notification endpoint

1

Copy the URL

Settings, then Analytics. The card carries the URL to paste, as both the production and the sandbox URL.
2

Paste it into App Store Connect

General, then App Information, then App Store Server Notifications.
3

Send Apple's test notification

The card shows the last notification’s type, so “TEST” arriving is the end-to-end proof that the URL works.
Every body Apple posts is verified four ways before anything is written: the certificate chain ends at one of Apple’s root certificates compared byte for byte against vendored copies, every certificate is signed by the one above it and inside its validity window, the leaf carries Apple’s App Store notification OID, and the ES256 signature verifies against the leaf’s key. Beside the received count the card shows how many bodies arrived with a signature Orbit refused; a number above zero there is almost always this URL pasted into another app’s entry. Rotating mints a new URL and stops the old one in the same statement. Apple keeps posting to the old one until App Store Connect is updated, and those notifications are lost, which the button says before it is pressed.

The App Store Connect key, and the role it needs

The same key that reads Release and pulls TestFlight crashes also runs the nightly analytics reports sync. The role it needs here is not the role Release needs, and that is the usual reason the App Store block says nothing arrived. A key with only Developer or App Manager reads Release perfectly and gets a 403 here, so the two syncs record their outcomes separately and the card shows Apple’s own message. Two dates are Apple’s schedule, not a delay Orbit adds: the first report of a new request appears 24 to 48 hours after it is created, and a day is only final two days after it ends. The newest App Store day is always a couple of days behind today.
Apple counts only the devices whose owner agreed to share analytics with Apple and with developers. Orbit counts every device the SDK runs on. The App Store block never adds the two or divides one by the other: it puts them on one line and says why they differ.

Reports

  • The weekly digest goes out Monday 09:00 in the project’s own time zone, on the alert channels the project already has. Choose the zone and tick the channels on the Reports tab, beside a preview built from the same fold the Monday job runs, so you can read what you would get before choosing a channel. Nothing is sent until at least one channel is ticked. The week identifies a digest, never the minute it was sent, so an hour when Orbit was down costs freshness and never the week.
  • Saved queries are one metric, one window and one optional breakdown and filter: exactly what the console reads, the CLI reads with orbit analytics query, and an Ask analytics answer writes down. Pro+ keeps 25; Ultra is unlimited.
  • Export writes any table on the page as CSV, over the window and unit the page is reading, with a day the rollup has not written as an empty cell rather than a zero. Revenue is the one table with a gate: a month is exported only once it is settled, meaning the month is over, Apple’s reports for its last days are final, the nightly reconcile has looked at it since, and it found no transaction one source has and another does not. A month left out never becomes a silent zero: the file says which month and what is missing.

Ask analytics costs credits

It is the one action on this page that spends, and it shows the price first: type a question, read the estimate (what it costs, what is left, which model), then confirm. The answer is written from this project’s own rolled-up numbers and the catalogue of metrics Orbit can answer, and it names the query it used, so you can open it as a chart beside the words. An answer nobody can check against the numbers is the failure mode this surface exists to avoid. An answer that says no single metric answers the question is an honest answer and you will get one. Every answer stays in the history with its query, its confidence and what it cost, so the next person does not pay for the same question twice. Everything else on the page is free on every plan.

Where the numbers come from

The nightly and hourly rollups, never the raw event stream, which is why the page loads in two requests whatever the fleet size. The rollup writes today every hour and yesterday every night, so the first numbers appear within the hour and retention on the days that follow. Raw events keep the project’s own 30 day retention. The rollups keep as long as your plan reads: 30 days on Free, 90 on Pro, 13 months on Pro+ and Ultra.