Amba
Prompts

Expo build prompt

Canonical /goal prompt for building a full Expo app with Amba as the only backend. Paste into Claude Code, Cursor, or Windsurf. Also available as the /amba-build skill.

Last reviewed: 2026-05-17. The canonical version of this page lives at docs.amba.dev/prompts/expo-build. If you're reading an inlined snapshot from your .claude/skills/amba-build/SKILL.md, check the URL above for updates.

This is the prompt an AI coding agent runs to build a full Expo app where Amba is the only backend. It's structured as a single /goal directive — paste it, replace <DESIGN_HASH> with whatever describes your design (a URL, a description, a Figma link), and let the agent execute.

Quick setup

The CLI handles signup, project provisioning, env-file writes, and MCP client config wiring in one command:

npx @layers/amba init --sandbox

That's the entire setup. The CLI:

  1. Signs up an agent-mode developer account (no browser, no email verification needed for sandbox).
  2. Creates an Amba project and mints a client key + admin PAT.
  3. Writes .env.local (AMBA_PROJECT_ID, AMBA_CLIENT_KEY, AMBA_API_URL).
  4. Writes AMBA.md (project-scoped context for the agent).
  5. Auto-wires mcpServers.amba into every MCP client config it detects on disk — Claude Code, Cursor, Windsurf.
  6. Prints a per-client restart instruction.

After restarting your MCP client, the Amba MCP toolset is available (amba_* tools — ~130 of them) and the agent can drive the backend end-to-end.

If you have the /amba-build skill installed (via npx @layers/amba init --sandbox), invoke it directly:

/amba-build <DESIGN_HASH>

Otherwise paste the prompt below.

Current known gotchas

Three remaining wrinkles you may hit. Everything else from the 2026-05 DX cascade is fixed.

  • Web CORS — the public API does not currently send Access-Control-Allow-Origin for browser-origin requests. Use the agent's circuit-break-on-second-failure rule for web targets; for Expo (iOS + Android) you'll never see this.
  • React Native bundle size — the React Native SDK adds ~4 MB to the JS bundle today. Functional, just heavier than the long-term goal. Tracked separately.
  • Sandbox MAU cap (50) — the agent-mode sandbox tier caps at 50 monthly active users. If you blow through it during testing, call amba_users_reset_sandbox to clear the counter — that tool exists specifically for this. Upgrade to the Free tier (claim the email) to lift the cap to 1,000.

How to read the design

If <DESIGN_HASH> is a URL to a packaged design (e.g. a download link from your design tool of choice), unpack it before you start:

mkdir -p design && cd design
curl -L "<DESIGN_HASH>" -o design.tar.gz
gunzip -c design.tar.gz | tar -x
ls
# Expected: README, chats/, project/ (or equivalent)

Read the README first — it should tell you what each subdirectory holds. The chats/ directory typically contains conversation logs that capture the design intent in dialog form; treat them as the authoritative source for tone and feature priorities. The project/ directory holds the structured asset graph (screens, components, styles).

If <DESIGN_HASH> is a freeform description (not a URL), skip the unpacking and treat the description text as the design brief.

Use Amba for everything

The rule: any feature that touches data, identity, scheduling, notifications, content, or social — use Amba. Don't reach for AsyncStorage-as-database, don't bring in Firebase / Supabase / your own server, don't roll a custom auth layer. The point of this build is that Amba covers it all.

Specifically: every feature in the design that needs a backend maps to an Amba primitive. If you can't find a fit, the rule is escalate in the gaps log (see the verification gate), not "ship without Amba." Skipping a primitive needs a written justification — the verification gate enforces this.

Feature → Amba primitive map

App featureAmba primitiveMCP tools
User accounts (anonymous + Apple + Google + email)Authamba_developer_signup (one-time bootstrap), Amba.signIn() SDK calls
Profile data (name, avatar, prefs)App usersamba_users_list, amba_users_get, amba_users_bulk_update
Daily content (tips, lessons, quotes)Content libraries + schedulesamba_content_libraries_create, amba_content_items_add, amba_content_schedules_create, amba_content_list_libraries, amba_content_list_items, amba_content_list_schedules
Push notificationsPush campaignsamba_push_campaigns_create, amba_push_campaigns_send, amba_push_send_test, amba_push_list_campaigns
User segments (e.g. inactive 7d, premium)Segmentsamba_segments_create, amba_segments_list, amba_segments_evaluate
Daily streaksStreaksamba_streaks_create, amba_streaks_list (call streaks.qualify() from the SDK to record activity)
XP and levelsXP rulesamba_xp_rules_create, amba_xp_rules_list, amba_users_get_xp
Achievements / badgesAchievementsamba_achievements_create, amba_achievements_list, amba_achievements_get
Challenges (time-limited goals)Challengesamba_challenges_create, amba_challenges_list, amba_challenges_list_participants
LeaderboardsLeaderboardsamba_leaderboards_create, amba_leaderboards_list, amba_leaderboards_get
In-app currency / virtual goodsEconomy (currencies + catalog + stores)amba_currencies_create, amba_catalog_items_create, amba_stores_create, amba_currencies_grant, amba_users_get_inventory
Social (friends, groups, feed, DMs)Social primitivesamba_create_group, amba_groups_list, amba_groups_update_member, amba_friendships_list (feeds + messaging via SDK: Amba.feeds.*, Amba.messaging.*)
Remote feature flags / configConfigsamba_configs_create, amba_configs_list, amba_configs_update
Entitlements (premium / paywall)RevenueCat / Superwall integrationamba_integrations_configure, amba_integrations_test
Custom data (anything not above)Collectionsamba_collections_create, amba_collections_alter, amba_collections_list, amba_admin_insert_row, amba_admin_list_rows, plus client-side Amba.collections.*
Analytics / event trackingEventsAmba.events.track(...) from the SDK; query with amba_analytics_get, amba_users_list_events

Every primitive above has list / read MCP tools you can use to verify seed data after creation — the verification gate uses these to catch "fake implementation" failure modes (where the app code thinks a thing was created but nothing actually landed in the backend).

Seed data

Before writing app code, seed the backend with enough data that every screen in the design has something realistic to render. Order:

  1. Configs — feature flags + tunable constants the app reads at boot (amba_configs_create).
  2. Segments — at least one (e.g. "new_user", first 7 days) so targeting works downstream.
  3. Content libraries + schedules — daily content for any tips/quotes/lessons screen. Seed ≥30 items so the carousel / day-stepper doesn't loop visibly.
  4. Streaks — define the streak shape (daily / weekly, grace window, freeze policy).
  5. XP rules — events → XP-award rules so XP accrues from real gameplay.
  6. Achievements — unlock criteria for badges.
  7. Challenges — at least one active challenge with rewards.
  8. Leaderboards — XP, streaks, or any custom metric.
  9. Currencies + catalog + stores — virtual currency, catalog items, store listings (only if the design has an economy screen).
  10. Collections — schemas + sample rows for any custom data the app needs (e.g. user-generated content, journal entries, custom list items).
  11. Push campaigns — at least one welcome push + one re-engagement push targeting your "new_user" segment.

After seeding, the verification gate (below) confirms each primitive exists by calling the matching amba_*_list MCP tool. Empty list → failure.

Engineering rules

These are non-negotiable. Violating any one of them fails the build gate.

  • Expo Router with typed routes. Use expo-router and enable experiments.typedRoutes in app.json. Every screen is a filesystem route; no manual navigation stacks.
  • TypeScript strict mode. strict: true in tsconfig.json. Zero any. Zero @ts-ignore. tsc --noEmit must pass.
  • React Native primitives only. View, Text, Pressable, ScrollView, FlatList, Image. No div, no span, no DOM-only libs. The build target is iOS + Android + Web — every screen has to render on all three.
  • Fonts via expo-font. Don't ship system-font-only screens; load the design's typography via useFonts and gate the splash screen on load.
  • Persistence via AsyncStorage. Anything you cache client-side (theme choice, last-viewed-item, dismissed banners) goes in AsyncStorage. Never sprinkle direct file I/O.
  • Theme system. A single theme.ts exports light + dark token maps; consume via a useTheme() hook. The verification gate toggles light ↔ dark and screenshots; if any screen has hardcoded colors that don't flip, the gate fails.
  • Circuit-break on second failure. If two consecutive Amba API calls fail with the same error, stop retrying and surface a clean empty-state to the user. Don't loop forever; don't crash. The web CORS issue (above) is the most likely trigger.
  • Deterministic offline fallback. When fetch fails (airplane mode, network drop), the app renders deterministic placeholder content — same content per userId + day — never random. Real data swaps in when the network returns.
  • Three-platform bundle gate. expo export --platform web, expo export --platform ios, and expo export --platform android must all succeed. If any one fails, the build fails. No "shipped iOS-only, web is broken" — the rule is parity.
  • Don't name a tab settings.tsx. Use account.tsx or preferences.tsx instead. Expo Router's static web export generates settings.html correctly but does not resolve direct URL navigation to /settings — the client-side router shows an unmatched-route error while other tab names work fine. (Observed in dogfood; upstream behavior, not an Amba issue.)

Verification gate

Before declaring the build done, run every check in this list. Any failure means the build is not done — fix and re-run.

# Type-check
pnpm tsc --noEmit
 
# Three-platform export
pnpm expo export --platform web
pnpm expo export --platform ios
pnpm expo export --platform android

Then, from inside the agent (use the Amba MCP tools):

  • amba_analytics_get → at least one event tracked end-to-end through Amba.events.track() from the app.
  • amba_users_list → at least one user exists (the agent's own anonymous signin counts).
  • For every primitive the seed step created, call the matching amba_*_list and assert non-empty:
    • amba_configs_list
    • amba_segments_list
    • amba_content_list_libraries, amba_content_list_items, amba_content_list_schedules
    • amba_streaks_list
    • amba_xp_rules_list
    • amba_achievements_list
    • amba_challenges_list
    • amba_leaderboards_list
    • amba_currencies_list (if economy seeded)
    • amba_catalog_list (if catalog seeded)
    • amba_collections_list + amba_admin_list_rows per collection
    • amba_push_list_campaigns
  • Empty list for any seeded primitive → the implementation is fake (UI exists but never wrote to the backend). Failure.
  • Manually walk every route in the browser (expo start --web), screenshot each, and confirm:
    • Light theme renders cleanly.
    • Dark theme renders cleanly (toggle and re-screenshot every route).
    • Empty states render when collections are empty (fresh-install simulation: wipe AsyncStorage, reload).
  • amba_users_reset_sandbox to confirm you can recover from the MAU cap if you blew past 50 during testing.

Skipping any primitive's seed step requires a one-line written justification in the gaps log (next section). "We don't need streaks" is fine; silence is not.

Final output

When done, write a final report to BUILD_REPORT.md in the project root. Required sections:

  • Start timestamp (when the agent started).
  • End timestamp (when the verification gate last passed).
  • MCP call inventory — every amba_* tool you invoked, with a count. Lets a human reviewer audit "did this agent actually use Amba for X" at a glance.
  • Primitive coverage table — one row per primitive from the Feature → Amba primitive map. Mark each ✅ (used), ⚠️ (used with caveats — explain), or ⏭ (skipped — justify in one line).
  • Gaps log — every primitive you skipped, every feature you couldn't fit cleanly to an Amba primitive, every workaround. One line per gap, no marketing language.
  • seed-report.json — machine-readable seed summary: { "primitive": "<name>", "created": <count>, "listed": <count> } for every primitive. The listed count comes from the amba_*_list call in the verification gate. created === listed for every row is the success condition.

If BUILD_REPORT.md is missing any required section, or seed-report.json is missing, the build is not done.

On this page