Expo Account, Projects & Permissions
Structure Expo accounts, EAS projects, and role-based access so teams can ship multiple apps under one organization - with clear ownership, credential isolation, and least-privilege permissions.
Search across all documentation pages
Structure Expo accounts, EAS projects, and role-based access so teams can ship multiple apps under one organization - with clear ownership, credential isolation, and least-privilege permissions.
Quick-reference recipe card - copy-paste ready.
# Log in and link a new app to an organization project
eas login
eas init// app.json - route the app to an organization and pin the EAS project
{
"expo": {
"name": "Retail App",
"slug": "retail-app",
"owner": "acme-mobile",
"extra": {
"eas": {
"projectId": "bd2f7e21-1ee7-47f2-8357-d7c4b50622fb"
}
}
}
}# Verify which account owns the linked project
eas project:infoWhen to reach for this:
// app.config.ts - multi-app org: shared projectId pattern, distinct slugs per app
import type { ExpoConfig } from "expo/config";
const APP_SLUG = "retail-consumer";
const ORG_OWNER = "acme-mobile";
const config: ExpoConfig = {
name: "Acme Retail",
slug: APP_SLUG,
owner: ORG_OWNER,
version: "3.1.0",
ios: {
bundleIdentifier: "com.acme.retail",
},
android: {
package: "com.acme.retail",
},
extra: {
eas: {
projectId: "bd2f7e21-1ee7-47f2-8357-d7c4b50622fb",
},
appFamily: "retail",
},
};
export default config;// eas.json - profiles inherit org credentials via the linked projectId
{
"cli": {
"version": ">= 16.0.0",
"appVersionSource": "remote"
},
"build": {
"development": {
"developmentClient": true,
"distribution": "internal",
"environment": "development"
},
"preview": {
"distribution": "internal",
"environment": "preview"
},
"production": {
"distribution": "store",
"environment": "production",
"autoIncrement": true
}
},
"submit": {
"production": {
"ios": {
"appleTeamId": "ABCDE12345"
}
}
}
}# CI: use a robot token with minimal scope - not a personal password
# Expo dashboard → Account Settings → Access Tokens → Create token
export EXPO_TOKEN="xxxxxxxxxxxxxxxxxxxxxxxx"
eas build --profile production --platform ios --non-interactiveWhat this demonstrates:
owner routes the app to an organization - builds, credentials, and env vars scope to acme-mobile.projectId is the durable key EAS services use across slug or bundle ID changes.| Type | Best for | Collaboration |
|---|---|---|
| Personal | Solo projects, learning, prototypes | Share credentials manually (discouraged) |
| Organization | Teams, agencies, multi-app companies | RBAC, shared credentials, audit logs |
Every expo.dev signup creates a Personal account. Create an Organization when more than one person needs build, submit, or credential access.
eas init → creates/links project under active account
→ writes extra.eas.projectId to app config
→ EAS Build / Update / env vars key off projectId
slug is human-readable and used in URLs - it can change.projectId is a UUID - stable for the lifetime of the project.owner determines which account's credential store and billing apply.| Role | Builds & updates | Credentials | Billing & invites | Delete account/projects |
|---|---|---|---|---|
| Owner | ✓ | ✓ | ✓ | ✓ |
| Admin | ✓ | ✓ | ✓ (no Owner grant) | ✗ |
| Developer | ✓ | ✓ | ✗ | ✗ |
| Viewer | View in Expo Go only | ✗ | ✗ | ✗ |
Assign Developer for engineers who ship daily. Reserve Owner for leads and billing contacts. Use Viewer for stakeholders who only need to open preview builds in Expo Go.
| Pattern | Structure |
|---|---|
| One org, many projects | acme-mobile org with retail-app, warehouse-app, driver-app - each with its own projectId |
| Org per client (agency) | client-a-mobile org isolates credentials and billing per customer |
| Account-wide env vars | Shared NPM_TOKEN or Sentry org token at account scope; app-specific API URLs at project scope |
| Escrow transfer | Temporary org holds a project during acquisition - both parties need Owner on escrow |
// Reading linked project metadata at runtime (diagnostics only - not for auth)
import Constants from "expo-constants";
const projectId = Constants.expoConfig?.extra?.eas?.projectId;
const owner = Constants.expoConfig?.owner; // available in static config
export function DiagnosticsBanner() {
if (!__DEV__) return null;
return (
<Text>
EAS {projectId ?? "unlinked"} · owner: {owner ?? "personal"}
</Text>
);
}Never use projectId or owner for in-app authorization - they are build-time identifiers, not security boundaries.
owner on org projects - eas build may target your personal account instead of the team. Fix: Set "owner": "org-slug" in app.json before eas init or transfer the project.eas credentials for rotation.projectId per app binary (bundle ID / package).| Alternative | Use When | Don't Use When |
|---|---|---|
| Organization account | Any team ≥ 2 people | Solo hobby project with no CI |
| Personal account | Learning, single-developer prototypes | Production apps with shared credentials |
| One org per client | Agency isolation and offboarding | Single-product company - unnecessary overhead |
| Account-wide env vars | Shared tokens across all org apps | App-specific API URLs - use project scope |
| Personal access token (CI) | Quick CI setup on a small team | Long-lived production pipelines - prefer robot tokens |
| Convert personal → org | Solo dev becoming a team | You need a clean client separation - create a new org instead |
When anyone besides you needs to run builds, manage credentials, configure environment variables, or submit to app stores. Organizations provide RBAC, shared credential stores, and member management.
It sets which Expo account (personal username or organization slug) owns the project. Builds, credentials, and dashboard URLs resolve under that account. Required when the project belongs to an organization you are a member of.
A stable UUID written by eas init that links your repo to an EAS project. EAS Build, Update, environment variables, and credentials all scope to this ID - not to slug or bundle identifier.
eas login
eas initFollow prompts to create a new project or link to an existing one. The command writes projectId into your app config.
The active eas whoami account or a missing owner field defaults to personal scope. Set "owner": "your-org-slug" in app config and confirm eas project:info shows the organization.
Yes - User Settings → "Convert your account into an organization." Projects, credentials, EAS subscriptions, and webhooks transfer. Plan a maintenance window and confirm integrations using your access token still work.
Project Settings → General → Transfer project. You must be Owner or Admin on both source and destination accounts. For transfers to a third party without shared access, use an escrow organization.
Owners and Admins. Admins can assign any role except Owner. Owners can assign any role including Owner.
Create an access token (robot user for organizations). Set EXPO_TOKEN in CI secrets. Use --non-interactive on eas build and eas submit. Do not store personal passwords in CI.
Technically yes, but not recommended. Credentials, update channels, and environment variables collide. Use one EAS project per distinct app (unique bundle ID / package).
Developer role covers eas build, eas update, eas credentials, and environment variable reads (per visibility). Developers cannot manage billing, invite members, or change org settings.
Create a separate Organization per client (client-a-mobile, client-b-mobile). Each org holds its own projects, credentials, and billing. Developers receive invites only to the orgs they serve.
Account Overview → User settings → Security activity. Shows password, email, and 2FA changes. Organization Owners should enforce 2FA for all members on production accounts.
eas init and early project linkingowner, slug, and extra configurationStack versions: This page was written for React 19.2.3, React Native 0.86.0, and Expo SDK 57 (
expo~57.0.4).
Reviewed by Chris St. John·Last updated Jul 16, 2026