Amba

Quickstart

Get Amba running in your Expo app in 5 minutes.

Not on Expo? This page is for the Expo managed workflow. If you're building with bare React Native, see SDK → React Native for the bare-RN setup (pod install, react-native-config env, Apple/Google sign-in via the platform-native libraries). The runtime API is identical — only install + native config differs.

1. Install the SDK

npx create-expo-app@latest my-app --template default
cd my-app
npx @layers/amba init

@layers/amba init walks you through:

  • Creating an Amba account (or logging in).
  • Creating a project.
  • Generating API keys.
  • Writing .env.local, AMBA.md, and .cursor/rules/amba.mdc to your project.

Then install the Expo SDK:

npx expo install @layers/amba-expo @react-native-async-storage/async-storage \
  expo-notifications expo-device expo-apple-authentication \
  expo-auth-session expo-crypto expo-linking

If you already have an account:

npx @layers/amba login

Rename env vars for Expo

Expo only exposes env vars prefixed with EXPO_PUBLIC_ to client code. Copy the CLI output into your app's .env.local with the prefix:

EXPO_PUBLIC_AMBA_PROJECT_ID=<your project id>
EXPO_PUBLIC_AMBA_CLIENT_KEY=<your client key>
EXPO_PUBLIC_AMBA_API_URL=https://api.amba.dev

2. Initialize the client

@layers/amba-expo provides an Amba singleton that handles storage, push tokens, and social sign-in for you. Call init() once in your root layout:

// app/_layout.tsx
import { useEffect } from 'react';
import { Slot } from 'expo-router';
import { Amba } from '@layers/amba-expo';
 
export default function RootLayout() {
  useEffect(() => {
    Amba.configure({
      projectId: process.env.EXPO_PUBLIC_AMBA_PROJECT_ID!,
      clientKey: process.env.EXPO_PUBLIC_AMBA_CLIENT_KEY!,
    }).catch((err) => console.warn('Amba.configure failed', err));
  }, []);
 
  return <Slot />;
}

init() restores the session from persistent storage, creates an anonymous id, fetches remote config, and (by default) registers the device push token.

3. Register the Expo config plugin

In app.json, add @layers/amba-expo to the plugins array. The plugin wires up iOS aps-environment, Apple Sign In capability, URL schemes, and Android intent filters — no hand-editing Info.plist / AndroidManifest.xml.

{
  "expo": {
    "plugins": ["expo-router", ["@layers/amba-expo", { "ios": { "pushNotifications": true } }]]
  }
}

Push on iOS requires a development build (not Expo Go) because custom entitlements aren't supported in Expo Go.

4. Track your first event

import { Amba } from '@layers/amba-expo';
 
function HomeScreen() {
  return (
    <Button
      title="Complete Workout"
      onPress={() =>
        Amba.events.track('workout_completed', {
          duration_minutes: 30,
          type: 'strength',
        })
      }
    />
  );
}

Events are sent to POST /client/events and automatically:

  • Update the user's last_seen_at.
  • Qualify any matching streaks.
  • Trigger XP rules.
  • Generate activity feed items (if configured).

5. Create a push campaign via MCP

If you're using an AI agent (Cursor, Claude Code), add the Amba MCP server:

{
  "mcpServers": {
    "amba": {
      "command": "npx",
      "args": ["@layers/amba-mcp"],
      "env": {
        "AMBA_API_KEY": "YOUR_ADMIN_API_KEY"
      }
    }
  }
}

Then ask your AI agent:

"Create a push notification campaign called 'Welcome Back' that targets users who haven't opened the app in 7 days"

The agent will use amba_segments_create and amba_push_campaigns_create to set it up.

6. Check project status

npx @layers/amba status

This shows your project health: active users, push token count, segment count, and integration status.

Alternative: manual signup via the API

If you'd rather script signup (CI tasks, internal dashboards), the API exposes the same flow:

Sign up

POST /auth/developer/signup
Content-Type: application/json
 
{
  "email": "you@example.com",
  "password": "at-least-8-chars",
  "name": "Jane Dev"
}

Returns 201 with data.access_token, data.refresh_token, and data.developer. Passwords below 8 characters return WEAK_PASSWORD; duplicate emails return 409 EMAIL_EXISTS.

Log in

POST /auth/developer/login
 
{ "email": "you@example.com", "password": "..." }

Same response shape. Invalid creds return 401 INVALID_CREDENTIALS.

Rotate tokens

Refresh tokens live 30 days. Every refresh rotates both the access token and the refresh token — the old session is revoked atomically, so a replayed refresh is detected.

POST /auth/developer/refresh
 
{ "refresh_token": "<jwt>" }

Fetch the current developer

GET /auth/developer/me
Authorization: Bearer <access_token>

Log out

POST /auth/developer/logout
 
{ "refresh_token": "<jwt>" }

Logout is idempotent — an invalid token still returns { data: { success: true } }.

Create a project

POST /admin/projects
Authorization: Bearer <access_token>
 
{ "name": "My App", "environment": "development" }

Provisioning runs in the background. Poll the response until project status flips to active.

Mint an API key

Client SDKs authenticate with X-Api-Key. Keys are per-project and scoped to either development or production.

POST /admin/projects/:projectId/api-keys
Authorization: Bearer <access_token>
 
{ "name": "Expo dev key", "environment": "development" }

The response includes the plaintext key once — store it immediately. Rotating a key creates a new row and leaves the old key valid until you explicitly revoke it.

Rate limits

Developer auth is rate-limited per IP to catch credential stuffing:

EndpointPer minutePer day
signup550
login10100
refresh30

Exceeding any window returns 429.

What's next?