Report screen views, sessions and push opens over HTTP, and read the analytics aggregates back.
Base URL: https://app.nativenotify.com
The native-notify SDK calls the reporting endpoints for you. Call them yourself from custom clients such as web apps and backends.
Report screen views
POST /api/analytics/screen
{
"appId": 123,
"appToken": "yourAppToken",
"screenName": "Home",
"deviceId": "optional-stable-device-id",
"token": "ExponentPushToken[...]",
"subId": "optional-indie-sub-id"
}
Or batch 1 to 50 events per request:
{
"appId": 123,
"appToken": "yourAppToken",
"events": [
{ "screenName": "Home", "deviceId": "optional-stable-device-id" },
{ "screenName": "Settings", "deviceId": "optional-stable-device-id" }
]
}
screenNameis required per event and trimmed to 120 characters.- For unique views, send
deviceId(best),subIdortoken. With none, the view counts toward totals only. In a batch, put them inside each event: top-level device keys are ignored. - Answers
201 {"ok": true, "accepted": N},400for missing or invalid fields,401for a bad app token.
Report sessions
POST /api/analytics/session
{
"appId": 123,
"appToken": "yourAppToken",
"sessionId": "unique-session-id",
"durationMs": 45000,
"deviceId": "optional-stable-device-id"
}
sessionId is required and deduplicated per app, so the same id counts once. Longer ids are cut to 80 characters, and durationMs is clamped to 24 hours. Answers 201 {"ok": true}, or 400 {"error": "sessionId is required"}.
Report notification opens
POST /api/notification/opened
{
"appId": 123,
"appToken": "yourAppToken",
"notification_id": "1000000412",
"deviceId": "optional-stable-device-id"
}
notification_id is the nn_notification_id the push carried. Every report adds to opened. A deviceId (or subId / token) also counts it toward opened_unique, once per device. Answers 201 {"ok": true}, 400 when appId, appToken or notification_id is missing, and 404 {"error": "Notification not found for this app"} for an id this app never sent.
Read aggregates
GET with the app credentials in the path. Out-of-range values are clamped (days to 1–365).
| Endpoint | Query | Returns |
|---|---|---|
/api/analytics/summary/:appId/:appToken | days (default 30) | Growth and a daily series, below |
/api/analytics/screens/:appId/:appToken | days (default 30), limit (1–25, default 10) | screens: [{ screen_name, total_views, unique_views, series: [{ day, total, unique_views }] }], ranked by total views. unique_views counts distinct devices across the range. |
/api/analytics/retention/:appId/:appToken | weeks (1–52, default 8) | cohorts: weekly cohorts by first-activity week, each with size and per-week week, users, pct |
/api/analytics/best-times/:appId/:appToken | days (default 90) | cells: [{ dow, hour, opened }] (0 = Sunday, hours in UTC), and the total opens |
/api/notification/failures/:appId/:appToken | days (default 30) | errors: [{ error_code, count, last_seen }] from Expo receipt errors (DeviceNotRegistered, InvalidCredentials, …) and push API transport codes (APNs:*, FCM:*, WebPush:*) |
/api/notification/stats/:appId/:appToken | take (default 10), skip | Per-notification rollup, below |
/api/analytics/export/:appId/:appToken | type, days (default 30) | A text/csv download, below |
Summary
{
"days": 30,
"seriesSource": "live",
"current": { "dau": 120, "wau": 480, "mau": 1300, "newUsers": 210, "returningUsers": 1090, "sessions": 5400, "avgSessionMs": 64000, "stickiness": 0.09 },
"previous": { "dau": 110, "wau": 450, "mau": 1250, "sessions": 5100 },
"growth": { "dau": 9.1, "wau": 6.7, "mau": 4, "sessions": 5.9 },
"series": [{ "day": "2026-10-01", "dau": 118, "wau": 470, "mau": 1290, "newUsers": 7, "returningUsers": 111, "sessions": 180, "avgSessionMs": 61000 }]
}
In current, dau, wau and mau are rolling 1, 7 and 30 days, newUsers and returningUsers cover the trailing days, and stickiness is DAU ÷ MAU. previous is one period earlier, and growth is its percent change, null with no baseline. seriesSource is "live" (from raw activity, days ≤ 90) or "rollup" (the nightly rollup, for longer ranges).
Per-notification stats
Rows are most recently updated first, with the total in the X-Total-Count header. Each has notification_id, source ("mass", "indie" or "universal"), accepted, delivered, failed, opened, opened_unique, first_opened_at, last_opened_at, updated_at, date and title.
CSV export
An unknown type falls back to summary.
type | Columns |
|---|---|
summary (default) | day, dau, wau, mau, new_users, returning_users, sessions, avg_session_seconds. At most the last 90 days, whatever days asks for. |
screens | screen_name, day, total_views, unique_views |
sends | notification_id, source, date, title, accepted, delivered, failed, opened, opened_unique. Mass, individual and push API sends, at most 1000 rows. |