Docs/Guides/Observabilityview as markdown
Observability: reports, reviews, and metrics from the terminal
The observation half of Flightline reads everything App Store Connect knows about your app: sales, finance, subscriptions, customer reviews, analytics, TestFlight feedback, crash diagnostics, and performance metrics. Every command here is read-only, supports --output json, and is designed to be piped. The examples use Tideterm (app.tideterm.ios); substitute your own bundle ID.
Prerequisites
- Credentials configured per the API key guide.
APP_STORE_CONNECT_VENDOR_NUMBERexported for sales, finance, and subscription reports (find it in App Store Connect under Payments and Financial Reports). The commands refuse to run without it rather than failing on the wire.- Finance reports require a key with the Admin role. Everything else works with App Manager.
Sales reports
Pull Sales and Trends data from /v1/salesReports:
flightline sales app.tideterm.ios --days 30
flightline sales app.tideterm.ios --week 2026-06-14
flightline sales app.tideterm.ios --month 2026-05
flightline sales app.tideterm.ios --year 2025
--days, --week, --month, and --year are mutually exclusive; with no date flag you get the last 7 days. The table view is a per-day summary:
DATE UNITS PROCEEDS CURRENCY
2026-06-29 14 27.86 USD
2026-06-30 9 17.91 USD
2026-07-01 22 43.78 USD
Two things to know about how Apple serves this data:
- Reports are vendor-wide. Apple does not filter by app on the wire; the bundle ID argument scopes the typed output client-side so a multi-app account stays focused.
- Daily windows cost one API call per day. A
--days 30pull is 30 calls against Apple's 500 requests/hour cap. Data lags about one day, so windows end yesterday. - No-report dates are normal. Apple returns HTTP 404 for quiet or not-yet-ready dates. Flightline records those dates in
unavailableDatesand continues processing every available date in the window.
Flightline resolves the app first and filters vendor-wide rows using its bundle ID, configured SKU, and Apple ID. This matters when the SKU does not equal the bundle ID. --week accepts any date in the desired week and normalizes it to Apple's required ending Sunday.
--report-type selects the report (default SALES; also SUBSCRIPTION, SUBSCRIPTION_EVENT, SUBSCRIBER, INSTALLS), and --report-sub-type the granularity (default SUMMARY). For raw data, --output tsv streams Apple's gunzipped wire format unfiltered:
flightline sales app.tideterm.ios --days 1 --output tsv > today.tsv
Finance reports
Settlement reports from /v1/financeReports. Apple indexes each report by fiscal year/month, which may not match the calendar month represented by the report rows:
flightline finance app.tideterm.ios --month 2026-05
flightline finance app.tideterm.ios --month 2026-05 --region US
flightline finance app.tideterm.ios --month 2026-05 --report-type FINANCE_DETAIL
--month YYYY-MM is required and means Apple's fiscal year/month. FINANCIAL defaults to consolidated region ZZ; FINANCE_DETAIL defaults to Z1. The table includes the actual row period and summarizes by country and settlement currency:
PERIOD COUNTRY CURRENCY QTY PARTNER_SHARE EXT_PARTNER_SHARE
05/01/2026..05/31/2026 DE EUR 31 1.39 43.09
05/01/2026..05/31/2026 US USD 204 1.39 283.56
--report-type accepts FINANCIAL (default) or FINANCE_DETAIL, and --output tsv passes through the raw report.
Subscription reports
Time-series subscription data, distinct from subscriptions list/get (which read product configuration):
flightline subscriptions reports app.tideterm.ios --type summary --range P30D
flightline subscriptions reports app.tideterm.ios --type events --range P7D
flightline subscriptions reports app.tideterm.ios --type retention --month 2026-05
--type maps to Apple's report types: summary (active counts and proceeds), events (cancels, upgrades, downgrades), retention (subscriber-level rows). --range is an ISO-8601 duration ending yesterday (P1D, P7D, P14D, P30D, P1M, P90D, P1Y); --month YYYY-MM pulls a single monthly report instead. Like sales, daily ranges make one call per day and --output tsv is available.
Customer reviews
flightline reviews list app.tideterm.ios
flightline reviews list app.tideterm.ios --rating 1..2 --territory USA
flightline reviews list app.tideterm.ios --since 30d --limit 50
flightline reviews get 6e2b9b14-1234-4567-8910-abcdef012345
flightline reviews summary app.tideterm.ios
Filters on list:
| Flag | Accepts |
|---|---|
--rating |
A single rating (1) or a range (1..3) |
--territory |
ISO 3166-1 alpha-3 code (USA, GBR) |
--since |
A duration (30d, 12h) or ISO date (2026-06-01) |
--limit |
Max reviews to emit (0 = no cap) |
RATING DATE TERRITORY TITLE ID
★★☆☆☆ 2026-06-28 USA Tide times wrong after update a3f8c210-...
★★★★★ 2026-06-27 GBR Best tide app I have used 99d1e4b7-...
get includes your developer response when one exists. summary reads Apple's per-locale AI summarization of recent reviews; apps Apple has not generated a summary for get a typed note field instead of an error.
Triage one-star reviews with an LLM in a single pipe:
flightline reviews list app.tideterm.ios --rating 1 --since 30d --output json \
| jq '[.reviews[].attributes | {title, body, territory}]' \
| llm "Cluster these App Store reviews by root cause and rank by frequency"
Analytics reports (async)
Analytics is Apple's only asynchronous report surface: you request a report, Apple generates it over minutes to hours, then you download the result. Flightline persists the request state to $XDG_STATE_HOME/flightline/<bundleId>/analytics.json, so every step is resumable; a Ctrl-C or dropped connection never loses the request.
One-shot with --wait
flightline analytics request app.tideterm.ios --access-type ONE_TIME_SNAPSHOT --wait
--wait blocks until Apple's reports are available, then prints them. --access-type is required: ONE_TIME_SNAPSHOT for a point-in-time pull, or ONGOING for a standing request. Because ONGOING requests never auto-terminate, --wait with ONGOING requires --max-duration (for example --max-duration 10m) as an upper bound.
Step by step
The same lifecycle as separate commands, useful in scripts and when the wait is long:
# 1. Submit; the request ID persists to the state file immediately
flightline analytics request app.tideterm.ios --access-type ONE_TIME_SNAPSHOT
# 2. Check on it any time later, from any shell
flightline analytics status app.tideterm.ios
# 3. Enumerate the report instances Apple produced
flightline analytics list-instances app.tideterm.ios --category APP_USAGE
# 4. Download every segment of an instance as CSV
flightline analytics download app.tideterm.ios --instance INST-42 --out ./reports/
status refreshes the request and every available report page from Apple before rendering. Pass --refresh=false only when an offline view of the local checkpoint is required:
FIELD VALUE
BUNDLE_ID app.tideterm.ios
STATE_FILE /Users/dev/.local/state/flightline/app.tideterm.ios/analytics.json
REQUEST_ID d5f0a9c2-...
STATUS reports_available
SUBMITTED_AT 2026-07-02T14:03:11Z
LAST_POLL_AT 2026-07-02T14:19:47Z
REPORTS 12
DOWNLOADED_SEGMENTS 0
list-instances narrows with --report-id, --category (for example APP_USAGE, COMMERCE), or --name-contains. download requires --instance and writes one CSV per segment, named <bundleId>-<instanceId>-segment<N>.csv; --out takes a directory (created if missing) and refuses to clobber an existing file.
If a --wait is interrupted, nothing is lost: the request ID was persisted at submit time, so status and list-instances pick up where it left off.
TestFlight beta feedback
Crash and screenshot feedback submitted by your testers:
flightline beta-feedback crash app.tideterm.ios
flightline beta-feedback screenshot app.tideterm.ios
flightline beta-feedback download CRASH-1234 --out crash.txt
flightline beta-feedback download SHOT-5678 --type screenshot
Both list commands take --since and --limit, plus --build to filter by build number (CFBundleVersion, the same value diagnostics and performance take). If a build number was reused, add --version and --platform to identify the release train. download fetches the crash log text or the first screenshot image for a submission; --type is crash (default) or screenshot, and --out names the destination file.
Crash and hang diagnostics
Apple deduplicates crash and hang reports into signatures: same call stack, one signature, regardless of user count. The API scopes them to a build, so --build (the build number, for example 87) is required:
flightline diagnostics list app.tideterm.ios --build 87
flightline diagnostics list app.tideterm.ios --build 2 --version 1.1 --platform IOS
flightline diagnostics list app.tideterm.ios --build 87 --type HANGS
flightline diagnostics get DIAG-SIG-1234 --output json
--type filters by DISK_WRITES, HANGS, or LAUNCHES. get fetches the full log payload for one signature; the complete stack trace is only in the JSON output, the table summarizes.
Performance metrics
The same battery, memory, hang, launch, and disk-write metrics as the Xcode Organizer Metrics tab:
flightline performance app app.tideterm.ios
flightline performance app app.tideterm.ios --category MEMORY
flightline performance build app.tideterm.ios --build 87
flightline performance build app.tideterm.ios --build 2 --version 1.1 --platform IOS
app is the cross-build aggregate; build is build-specific and requires --build (the build number). Reused build numbers require --version and --platform. Filter with --category (HANG, LAUNCH, MEMORY, DISK, BATTERY, TERMINATION, ANIMATION) and --device (Apple model ID, for example iPhone15,3). Apple needs enough user telemetry before metrics appear, typically 7 to 30 days after release; until then the command returns a typed note rather than an error.
Watch for regressions Apple has flagged:
flightline performance app app.tideterm.ios --output json | jq '.insights.regressions'
Composing it all
Every command emits stable JSON under --output json (the shape is a versioned contract; see JSON output), which makes the observation surface scriptable in three directions.
Pipe to jq:
flightline sales app.tideterm.ios --days 30 --output json \
| jq '[.summary[].units] | add'
flightline reviews list app.tideterm.ios --rating 1..2 --output json \
| jq -r '.reviews[].attributes.body'
Feed to an LLM. JSON output was designed as an LLM-ingestible surface: field names are self-describing and the envelope carries the request parameters, so a model gets full context from the payload alone.
flightline beta-feedback crash app.tideterm.ios --since 7d --output json \
| llm "Summarize the crash patterns and suggest which build introduced them"
Cron snapshots. Because everything is a single binary call with env-var auth, scheduled snapshots are one crontab line each:
0 9 * * * flightline sales app.tideterm.ios --days 1 --output json >> ~/tideterm/sales.jsonl
0 9 * * 1 flightline reviews summary app.tideterm.ios --output json > ~/tideterm/review-summary.json
See also
- JSON output contract, the envelope shapes and stability guarantees
- CLI reference, every command group;
flightline <group> --helpfor flags - The three-layer model, where observation fits in L1