NomaCMS

Blog

Headless CMS for mobile apps: one REST API for content and user sessions

Raşit Apalak
Raşit ApalakJuly 12, 2026

Mobile apps that show CMS-driven content often need logged-in users too: profiles, saved items, member-only articles, or in-app purchases tied to an account. The common pattern is a headless CMS for content plus Clerk, Auth0, or Firebase Auth for identity. That works, but it adds integration work on every new app.

NomaCMS is a headless CMS with auth built in. Your mobile client talks to one API base (https://app.nomacms.com/api) for both published content and project user sessions. This post explains how that fits a typical mobile stack and which credentials to use where.

What mobile apps usually need from a CMS

NeedTypical approachNomaCMS approach
Articles, settings, mediaContent APICollections, fields, assets over REST
User signup and loginSeparate auth vendorProject auth (/auth/* routes)
Member-only contentCustom backend glueSession tokens + your app logic
Images and filesCDN URLs from CMSAsset library with CDN delivery

Flutter, Swift, Kotlin, and React Native all work the same way here: HTTP requests with JSON. You do not need the JavaScript SDK on the device. Use it on a backend if you prefer, or call REST directly from the app through a thin API layer you control.

Two kinds of credentials (do not mix them up)

This trips up a lot of teams on day one.

Personal access token (your developer account)

  • Created in User Settings → API Keys in the NomaCMS dashboard
  • Used for Content API routes: collections, entries, assets, webhooks
  • Scoped to a workspace; create the key while that workspace is selected
  • Copy the full token when created (shown once; format is assigned by the dashboard)
  • Never ship this in a mobile app binary. Keep it on your server or in CI

Project user access token (your app's end users)

  • Issued after signup or login on /auth/signup and /auth/login
  • A JWT that identifies one user in one project
  • Used for /auth/me, logout, profile, and user API key management
  • Not a substitute for a personal access token on Content API routes

Same email in two projects means two separate accounts. Auth is isolated per project.

Fetch published content from a mobile app

The safest pattern: your mobile app calls your backend, and your backend calls NomaCMS with a personal access token.

Example server route (Node/Express style):

// Your API: GET /api/posts const response = await fetch("https://app.nomacms.com/api/posts?state=published", { headers: { "project-id": process.env.NOMA_PROJECT_ID!, Authorization: `Bearer ${process.env.NOMA_API_KEY!}`, Accept: "application/json", }, }) const posts = await response.json()

Every Content API request needs:

project-id: <your-project-uuid>
Authorization: Bearer <personal-access-token>
Accept: application/json

Replace posts with your collection slug. Filter with query parameters your app needs (state=published, locale, paginate, and so on). See List Entries for the full list.

If you truly need the app to read public content without your server in the middle, treat that as a product decision. A read-only key still belongs in a proxy you control, not hard-coded in the app store build.

Sign up and sign in from mobile

Project auth routes live under /auth/ on the same API base.

Sign up

POST https://app.nomacms.com/api/auth/signup project-id: <your-project-uuid> Content-Type: application/json { "email": "[email protected]", "password": "at-least-8-chars", "display_name": "Alex" }

A successful response returns access_token, optional refresh_token, and a user object. Some projects require email verification first. In that case you get 202 Accepted with verification_required: true instead of tokens. Your app handles the verification flow.

Sign in

POST https://app.nomacms.com/api/auth/login project-id: <your-project-uuid> Content-Type: application/json { "email": "[email protected]", "password": "at-least-8-chars" }

Store tokens in the platform secure store (Keychain on iOS, EncryptedSharedPreferences or Keystore on Android). Refresh before expiry:

POST https://app.nomacms.com/api/auth/refresh project-id: <your-project-uuid> Content-Type: application/json { "refresh_token": "<stored-refresh-token>" }

Social login

Google sign-in is supported today via OIDC id_token exchange:

POST https://app.nomacms.com/api/auth/login/social project-id: <your-project-uuid> Content-Type: application/json { "provider": "google", "id_token": "<id-token-from-google>" }

Complete the OAuth flow in the mobile SDK, then send the id_token to NomaCMS from your backend when possible.

Load the signed-in profile

GET https://app.nomacms.com/api/auth/me project-id: <your-project-uuid> Authorization: Bearer <project-user-access-token> Accept: application/json

Use this on app launch to restore session state.

User API keys (for your own backends, not Content API)

Signed-in users can create user API keys (uak_ prefix) for integrations you build. These are not Content API tokens. Sending a uak_ key to collection or entry routes returns 401.

List and create keys while authenticated as the project user. Store plain_text_key securely when created; it is only shown once.

Assets and images

Upload and list assets through the Content API at /api/files (from your server with a personal access token). Responses include url, thumbnail_url, and original_url you can render in mobile image views. See List Assets and Upload an Asset.

When a hosted CMS + auth beats stitching vendors

Consider NomaCMS for mobile when:

  • You ship content-driven apps (news, courses, community, catalogs) and need accounts
  • You want one API contract for your iOS and Android teams
  • You prefer a hosted backend over running Strapi or Payload yourself
  • You are an indie team that cannot afford a CMS bill plus a separate auth bill on every prototype

You may still want a dedicated auth vendor if you need enterprise SSO across many products. For many indie and small product teams, project auth in the same platform is enough to ship.

What to explore next

Every plan includes a 7-day free trial.

Learn more