Which PostHog OAuth scopes does a read-only integration need?
· By Klaus Siebeneicher
A read-only PostHog integration that shows dashboards needs five scopes: user:read, project:read, dashboard:read, insight:read and query:read. Anything beyond that should map to a specific screen or feature, and none of it needs a scope ending in :write. This is how MobileHog connects, and what we learned building it against PostHog Cloud in September 2026.
The five scopes and what each one is for
| Scope | Used for |
|---|---|
user:read |
Confirming who signed in (GET /api/users/@me/) |
project:read |
Listing organizations and projects the token can see |
dashboard:read |
Dashboards and their tiles |
insight:read |
Saved insights |
query:read |
Every number: tile results, web analytics, events (POST /api/projects/:id/query/) |
query:read is the one people forget. Dashboard and insight endpoints return the definitions; the numbers come from the query API. Without it, a dashboard loads with empty tiles.
Optional scopes: one screen each
Everything else in PostHog has its own read scope. MobileHog requests seven more, and each unlocks exactly one screen: error_tracking:read, feature_flag:read, experiment:read, survey:read, session_recording:read, cohort:read and annotation:read. If a user leaves one out (possible with personal API keys), that screen names the missing permission and the rest of the app keeps working. The full table, including the API each scope calls, is on the security page.
A rule worth keeping: every scope you request should map to an endpoint you actually call. A longer consent screen costs trust, and “reads only what it shows” stops being true the moment you ask for more.
No client registration: Client ID Metadata Documents
PostHog’s authorization server advertises client_id_metadata_document_supported: true. Instead of registering an app and receiving an ID, your client_id is an HTTPS URL you control. During sign-in, PostHog fetches the JSON document at that URL:
{
"client_id": "https://mobilehog.app/oauth-client.json",
"client_name": "MobileHog",
"logo_uri": "https://mobilehog.app/images/logo.png",
"redirect_uris": ["https://mobilehog.app/oauth/callback"]
}
Two practical consequences:
- You need a domain before sign-in can work. The
client_idmust match the document’s URL exactly, including the.jsonextension if you serve it statically. - Redirect URIs must be HTTPS. Custom URL schemes are not documented as supported, so a mobile app returns through a Universal Link (iOS) or App Link (Android) on that same domain.
PKCE, as a public client
A mobile app can’t keep a secret, so it signs in as a public client (token_endpoint_auth_method: none) with PKCE (S256). The flow is standard authorization code with a code verifier; there is no client secret to ship or leak.
One detail for iOS: don’t force an ephemeral browser session. PostHog’s sign-in page remembers the region you chose and your login. An ephemeral session throws both away and makes every sign-in a full manual one.
Tokens, regions and rotation
- Access tokens start with
pha_and last about ten hours. - Refresh tokens start with
phr_and rotate on use. Store the new one on every refresh, or the user is signed out after ten hours. - The OAuth host is not the API host.
oauth.posthog.comhandles sign-in for both regions but returns 404 for/api/. After the token exchange, probeus.posthog.comandeu.posthog.comand remember which one answers. - Tokens are scoped to what the user granted. The consent screen lets people grant all organizations, specific organizations or specific projects, so read the token’s scope before assuming which projects exist.
Be kind to the query budget
PostHog meters queries per project, and dashboard endpoints accept a refresh mode. Reading with force_cache returns cached results without recalculating; only a user action like pull to refresh should ask for blocking. Background polling spends the user’s own quota, and PostHog’s own guidance steers recurring use away from the query API.
If you want to see these choices in a shipping app, MobileHog’s guide walks through connecting, and the security page documents every permission.
FAQ
- Does PostHog require registering an OAuth client?
- No. PostHog supports Client ID Metadata Documents: the client_id is an HTTPS URL you control, and PostHog reads your client name, logo and redirect URIs from the JSON document at that URL during sign-in.
- Which PostHog host do OAuth tokens work against?
- Sign-in runs on oauth.posthog.com for both regions, but that host does not serve the API. API calls go to us.posthog.com or eu.posthog.com, so a client has to find out which region the account lives in after the token exchange.
- Do PostHog refresh tokens rotate?
- Yes. Each refresh returns a new refresh token and invalidates the old one, so a client must store the new token every time or the user is signed out when the access token expires.
// keep reading
- How to check your PostHog dashboards on an iPhone
Install MobileHog, sign in with your PostHog account and your dashboards appear, with every tile type. Step by step, plus how to read charts on a small screen.
- Does PostHog have a mobile app?
PostHog has mobile SDKs for tracking your own apps, but its dashboards live in the browser. Here is how to check PostHog on your phone, natively or in the browser.