Skip to content
MobileHogGet the app

Which PostHog OAuth scopes does a read-only integration need?

· By

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_id must match the document’s URL exactly, including the .json extension 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.com handles sign-in for both regions but returns 404 for /api/. After the token exchange, probe us.posthog.com and eu.posthog.com and 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.
  • 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.