# Upgrading your Project
Source: https://docs.a0.dev/advanced/upgrading-your-project
Learn why and how to upgrade your a0 project
## Why upgrades are necessary
To enable new capabilities and ensure compatibility with our mobile app, you need to upgrade your a0 project periodically. Our mobile app only supports the latest version of a0 so you need to update inorder to test your app.
## Runtime version and Expo SDK
a0 projects are built on top of Expo. In a0, you'll see a setting called **runtime version**, which is based on the Expo SDK version. When we upgrade a0 versions, the runtime version (and underlying Expo SDK version) in your project is also upgraded. The runtime version determines which Expo APIs, modules, and features are available to your app.
The latest a0 version uses Expo SDK 54 as of 11/18/25.
### Understanding runtime versions
Each runtime version includes:
* **Core Expo modules**: Camera, Location, Notifications, and other built-in capabilities
* **React Native version**: The underlying React Native framework version
* **Native module support**: Compatibility with native iOS and Android modules
* **API availability**: Which Expo APIs you can use in your project
### How runtime version upgrades work
When you upgrade your a0 project:
1. **Runtime version updates**: Your project's runtime version is upgraded to match the latest supported version
2. **Package versions change**: All Expo-related packages are updated to versions compatible with the new runtime version
3. **React Native may update**: The underlying React Native version may change to match the Expo SDK requirements
4. **Native modules update**: Native iOS and Android modules are updated to work with the new runtime version
## When to upgrade
The preview on the website will prompt you when it's time to upgrade your project. You won't be able to create new builds without upgrading to the latest version of a0.
## How to upgrade
### Upgrade from the UI prompt
When an upgrade is available, you'll see a prompt in the UI. Simply follow the on-screen instructions to upgrade your project.
The upgrade prompt will appear automatically when a new version is available and your project needs to be updated.
### Upgrade from Build Settings
You can also manually upgrade your runtime version from the Build Settings tab:
1. Navigate to your project's **Build Settings** tab
2. Find the **Runtime version** section
3. Select the latest available runtime version
4. Save your changes
You can only switch between the runtime version that was the latest when your project was created and the latest version. Intermediate runtime versions are not available for selection.
## Over-the-air updates
Over-the-air (OTA) updates allow you to push code changes to your app without requiring users to download a new version from the app store. However, there's an important limitation to understand.
### OTA update limitations
Over-the-air updates only work **within the same runtime version** that your app was built on. This means:
* **Code changes**: You can push JavaScript and asset updates via OTA within the same runtime version
* **Version upgrades**: You cannot upgrade the runtime version using OTA updates
* **New builds required**: To get all users on the latest runtime version, you must release a new build to the App Store or Play Store
OTA updates are great for quick bug fixes and feature updates, but they cannot change the underlying runtime version.
### When to release a new build
You'll need to create and release a new build to app stores when:
* You've upgraded your project to a new runtime version
* You change your projects build settings like App Icon or permissions,
If you upgrade your project to a new runtime version but don't release a new build, users with older app versions won't receive your over-the-air update.
## Why your app might be different after upgrading
If you notice changes in your app's functionality after upgrading, this is expected behavior. When we upgrade versions, the runtime version and the versions of packages in your app change, which can introduce changes in functionality.
The runtime version upgrade is a significant part of this process, as it updates the core framework your app runs on, including React Native and all Expo modules.
Package version updates often include bug fixes, performance improvements, and new features that enhance your app's capabilities.
Breaking changes or significant behavioral tweaks may require you to update your code to maintain the same functionality. Always test your project after upgrading it and make changes as necessary.
# New Chat
Source: https://docs.a0.dev/agent-guide/new-chat-explained
## What is a New Chat?
When you see "Chat history is too long", creating a new chat resets your conversation history while preserving your entire app. Your files, builds, and project settings remain unchanged.
## Why Do I Need a New Chat?
Chat histories can become very long over time. When they get too large, they can cause performance issues. Starting a new chat:
* **Fixes:** Chat loading and sending issues
* **Improves:** Response speed and reliability
* **Maintains:** Full access to your app and project
## How to Create a New Chat
For an Existing Project:
1. Navigate to your project
2. Click the **"New Chat"** button in the header
## What You Keep with a New Chat
When you create a new chat for an existing project, you keep **everything** important:
* **All your app files** (code, images, configuration)
* **Your app builds** and deployment history
* **Project settings** and integrations
* **App Store Connect** configurations
* **Supabase** database connections
* **All your published versions**
## Key Difference
**Resets:** Only your conversation history with the AI\
**Keeps:** Your entire app, files, builds, and project settings
**Creates:** A completely new project from scratch\
**Resets:** Everything. You start with a blank slate
## Common Questions
**No!** Your app, files, and all progress remain completely intact. Only the conversation history resets.
**Yes!** The AI can access all your current project files in the new chat, so it understands your app's current state.
**All preserved!** Your build history, deployed versions, and App Store submissions remain unchanged.
After about 15-20 messages (or 80,000 characters), or when the AI is making the same mistakes repeatedly, seems confused about your project structure, or you're switching to completely different features.
The AI can see all your current files in the new chat, so you can immediately ask it to continue working on features or fix issues without any setup.
# null
Source: https://docs.a0.dev/agent-guide/overview
## Why this matters
After analyzing millions of chats, we've identified several patterns that are stopping users from completing their app.
This guide breaks down exactly how to talk to a0.dev to get what you want.
## What you'll learn
The three things that really matter when sending a message
How to write prompts that get results, examples included
When and why to start a new chat while keeping your app
# null
Source: https://docs.a0.dev/agent-guide/three-principles
# Summary of the Principles
"Make it better" is not a prompt. List exactly what you want. One message should contain 3-5 small tweaks OR one big feature. Not both.
When something breaks, just copy in the error. Messages like 'fix the error' will likely not get you anywhere.
After 15-20 messages, the AI gets confused. Starting a new chat helps it continue on your app with a clear context.
## 1. Be Very Specific
A clear and articulated success criteria helps our agent deliver the best results that make you happy.
```text Good theme={null}
Update the login page:
1. Change button color from blue to #22C55E
2. Add "Remember me" checkbox below password field
3. Center the form vertically on the page
4. Add loading spinner when submitting
```
```text Bad theme={null}
Make the login page better
```
## 2. Show It The Error
When something breaks the easiest things to do is just copy the error from:
1. The bottom of the error screen within the app preview
2. Beneath the app preview inside the console logs
## 3. Make a New Chat
After about 15-20 messages (or 80,000 characters) our agent starts degrading in performance.
A good set of rules for when to use a new chat is if:
* The AI is making the same mistakes repeatedly
* It seems confused about your project structure
* You're switching from one feature to something completely different
* The conversation has become a debugging nightmare
Apps can contain unlimited chat sessions
# null
Source: https://docs.a0.dev/agent-guide/writing-prompts
## The anatomy of a good prompt
Every effective prompt has these elements:
### 1. Clear scope
State exactly what you want to change. Instead of "improve the form" try "add email validation to the signup form."
### 2. Specific details
Include colors, positions, text, behavior. Instead of "add a button" try "add a green 'Continue' button below the form that navigates to /dashboard."
### 3. Context when needed
If it's a bug, include the error. If it's a feature, explain where it fits in the flow.
### 4. One goal per message
Try not to combine "fix the login bug AND redesign the homepage AND add a new API endpoint." Picking one usually means it gets completed thoroughly.
## Examples that actually work
These prompts get results because they're clear, specific, and actionable.
```text theme={null}
I need to update the login screen:
1. Change the button color from blue to green
2. Add a "Forgot Password?" link below the login button
3. Make the email input field show a keyboard with @ symbol
4. Add validation to show error message if email format is wrong
```
```text theme={null}
Add a new settings screen with these options:
- Toggle for push notifications
- Dark mode switch
- User profile picture upload
- Logout button at the bottom
```
```text theme={null}
The profile save button throws this error:
TypeError: Cannot read property 'id' of undefined
at ProfileScreen.js line 45
Please fix by checking if user exists before accessing user.id
```
```text theme={null}
Build a tally counter app with the key output being UI design quality.
This app should have two components:
- The home page where users see all of their tallied items (quick +/- in this UI)
- The create tally item screen/modal which allows the user to set the name and other metadata
Lets build this in the neobrutalism style with a cartoonish minimalism design.
```
If you have an image or design reference, you can attach it to the chat! This helps the AI understand exactly what you're looking for visually.
# Android FCM Setup
Source: https://docs.a0.dev/development/android-fcm-setup
Set up Firebase Cloud Messaging credentials for Android push notifications
# Android FCM Setup
a0 uses Firebase Cloud Messaging (FCM v1) to deliver push notifications to Android devices. You need two files from Firebase:
| File | Purpose |
| -------------------------------- | ------------------------------------------------------------------------------ |
| **google-services.json** | Registers your Android app with Firebase. Injected into the APK at build time. |
| **Firebase service account key** | Private key used by a0's servers to send notifications via the FCM v1 API. |
## Step 1: Create a Firebase project
If you already have a Firebase project for your app, skip to Step 2.
1. Open the [Firebase Console](https://console.firebase.google.com/)
2. Click **Add project** and follow the setup wizard
## Step 2: Download google-services.json
This file contains your Firebase project's public configuration and is required for your Android app to connect to Firebase.
1. In the [Firebase Console](https://console.firebase.google.com/), open **Project settings** (gear icon)
2. Under **Your apps**, select your Android app (or click **Add app** → **Android** if you haven't added one yet)
3. Enter your Android package name — this should match the bundle identifier used in your a0 project
4. Click **Download google-services.json**
This file contains public-facing identifiers and is safe to commit to your repository. It does **not** contain any private keys.
## Step 3: Create a Firebase service account key
The service account key is a private JSON file that authorizes a0's servers to send push notifications on your behalf via the FCM v1 API.
1. In the Firebase Console, open **Project settings** → [**Service accounts**](https://console.firebase.google.com/project/_/settings/serviceaccounts/adminsdk)
2. Make sure **Firebase Admin SDK** is selected
3. Click **Generate New Private Key**
4. Confirm by clicking **Generate Key** — a JSON file will download
Store this file securely. It contains a private key that grants access to send notifications to your app. **Do not** commit it to your repository.
### Using an existing service account
If you already have a Google Cloud service account and want to reuse it for FCM:
1. Open the [IAM Admin page](https://console.cloud.google.com/iam-admin/iam) in Google Cloud Console
2. Find the service account you want to use and click the pencil icon to **Edit Principal**
3. Click **Add Role** and select **Firebase Cloud Messaging API Admin**
4. Click **Save**
5. Download a new JSON key for this service account if you don't already have one
## Step 4: Upload to a0
1. Open your project in a0
2. Go to **Project Settings** → **Build Settings** → **Android Credentials**
3. Upload your **google-services.json** file
4. Upload your **Firebase service account** key file
5. Build a new custom APK — the credentials will be included automatically
Both files will show a **Configured** badge once uploaded. You can replace or delete them at any time.
After uploading new credentials, you need to create a new APK build for the changes to take effect. Existing builds will continue using the credentials they were built with.
## Helpful links
* [Firebase Console](https://console.firebase.google.com/)
* [Firebase Service Accounts](https://console.firebase.google.com/project/_/settings/serviceaccounts/adminsdk)
* [Google Cloud IAM Admin](https://console.cloud.google.com/iam-admin/iam)
* [FCM v1 API documentation](https://firebase.google.com/docs/cloud-messaging/migrate-v1)
* [Push Notifications guide](/development/push-notifications)
# Deep Links
Source: https://docs.a0.dev/development/deep-links
Configure URL schemes to enable deep linking into your a0 app
# Deep Links
Deep links allow users to open your app directly from a URL, enabling features like email verification links, social sharing, and custom integrations.
Deep links use URL schemes (like `myapp://`) to open your app and optionally navigate to specific content within it.
## Overview
When you configure deep link schemes in your `build.yaml`, a0 automatically:
1. Registers the URL schemes with iOS (Info.plist)
2. Adds intent filters to Android (AndroidManifest.xml)
3. Configures Expo to recognize the schemes at runtime
Your app's bundle ID is automatically added as a scheme, so you always have at least one working deep link scheme without any configuration.
## Configuration
Add the `schemes` property to your `.a0/build.yaml` file:
```yaml theme={null}
general:
runtimeVersion: "exposdk:54.0.0"
schemes:
- myapp
- myapp-dev
ios:
versionName: "1.0.0"
android:
versionName: "1.0.0"
```
Schemes must be lowercase and start with a letter. They can only contain letters, digits, plus signs (+), hyphens (-), and dots (.).
## Handling Deep Links
Use the `expo-linking` library to handle incoming deep links in your app.
### Basic Setup
```tsx theme={null}
import * as Linking from 'expo-linking';
import { useEffect } from 'react';
export default function App() {
useEffect(() => {
// Handle deep link when app is already open
const subscription = Linking.addEventListener('url', ({ url }) => {
handleDeepLink(url);
});
// Handle deep link that opened the app
Linking.getInitialURL().then((url) => {
if (url) {
handleDeepLink(url);
}
});
return () => subscription.remove();
}, []);
const handleDeepLink = (url: string) => {
const parsed = Linking.parse(url);
console.log('Deep link received:', parsed);
// Navigate based on parsed.path and parsed.queryParams
};
return (
// Your app content
);
}
```
### Creating Deep Link URLs
Generate URLs that open your app:
```tsx theme={null}
import * as Linking from 'expo-linking';
// Create a URL with your custom scheme
const url = Linking.createURL('profile/123', {
scheme: 'myapp',
queryParams: { ref: 'email' }
});
// Result: myapp://profile/123?ref=email
```
### With React Navigation
If you're using React Navigation, configure deep linking in your navigation container:
```tsx theme={null}
import { NavigationContainer } from '@react-navigation/native';
const linking = {
prefixes: ['myapp://', 'myapp-dev://'],
config: {
screens: {
Home: '',
Profile: 'profile/:id',
Settings: 'settings',
},
},
};
export default function App() {
return (
{/* Your navigators */}
);
}
```
## Testing Deep Links
Use the `xcrun` command to test deep links on iOS:
```bash theme={null}
xcrun simctl openurl booted "myapp://profile/123"
```
Use `adb` to test deep links on Android:
```bash theme={null}
adb shell am start -a android.intent.action.VIEW -d "myapp://profile/123"
```
The easiest way to test on a physical device:
1. Send yourself a message (email, Slack, etc.) containing the deep link URL
2. Tap the link to open your app
## Common Use Cases
Send users a verification link that opens your app and marks their email as verified:
```tsx theme={null}
// In your email: myapp://verify?token=abc123
const handleDeepLink = (url: string) => {
const { path, queryParams } = Linking.parse(url);
if (path === 'verify' && queryParams?.token) {
verifyEmail(queryParams.token);
}
};
```
Allow users to reset their password from an email link:
```tsx theme={null}
// In your email: myapp://reset-password?token=xyz789
const handleDeepLink = (url: string) => {
const { path, queryParams } = Linking.parse(url);
if (path === 'reset-password' && queryParams?.token) {
navigation.navigate('ResetPassword', { token: queryParams.token });
}
};
```
Let users share specific content that opens directly in your app:
```tsx theme={null}
// Shared link: myapp://post/456
const handleDeepLink = (url: string) => {
const { path } = Linking.parse(url);
const match = path?.match(/^post\/(\d+)$/);
if (match) {
navigation.navigate('Post', { id: match[1] });
}
};
```
Track referrals by including a code in the deep link:
```tsx theme={null}
// Referral link: myapp://signup?ref=friend123
const handleDeepLink = (url: string) => {
const { queryParams } = Linking.parse(url);
if (queryParams?.ref) {
// Store referral code for attribution
storeReferralCode(queryParams.ref);
}
};
```
## Automatic Bundle ID Scheme
Your app's bundle ID (e.g., `dev.a0.apps.myapp`) is automatically registered as a URL scheme. This ensures you always have a unique, collision-free scheme available even without explicit configuration.
While the bundle ID scheme works, custom schemes like `myapp://` are shorter and more user-friendly for sharing.
# Payment Implementation
Source: https://docs.a0.dev/development/monetization/payment-implementation
Integrate subscriptions and one-time purchases into your a0 app with code examples
# Payment Implementation
Learn how to implement subscriptions and one-time purchases in your a0 app using the `a0-purchases` library.
The a0 payment system uses the [`a0-purchases`](https://www.npmjs.com/package/a0-purchases)
library. All apps built with a0 come with the necessary configuration and providers built in.
## Platform support and testing (important)
* Web: Stripe (test and production). You can test purchases in the browser.
* iOS: Apple App Store. You sync products to App Store Connect and test purchases via TestFlight.
* Android: not supported yet.
Apple payments do not work in the a0 app or Expo Go. To test iOS purchases, you must use
TestFlight or a native iOS build.
## Prerequisites
Before implementing payments in your code, ensure you have:
Defined your features and plans (either in the dashboard UI or by editing `.a0/monetization.yaml` via the Monetization tab), and created at least one offering. See the [Payment Setup](/development/monetization/payment-setup) guide.
Synced your plans to Stripe and/or Apple App Store Connect.
If you're **migrating an existing iOS subscription**, link your existing StoreKit product IDs in `.a0/monetization.yaml` (v2) so your paywall can resolve products immediately (without waiting on a successful first sync).
Created at least one offering and set it as current - this is required for paywalls to display products.
If you're using the a0 coding agent: it can edit `.a0/monetization.yaml` directly (same as `build.yaml` and `general.yaml`), then sync to Apple/Stripe. It should not tell you to do it yourself.
## One-time purchases (lifetime unlocks + consumables)
The same paywall + `purchase(packageId)` flow supports:
* **non-consumable** (buy once + restore): great for “lifetime unlock”
* **consumable** (buy many times): great for “credits”, “tokens”, etc
Configure this in `.a0/monetization.yaml` (v2) with `pricingTiers[].productType`:
```yaml theme={null}
version: 2
bundles:
- ref: pro
name: Pro
# Only required if you're linking existing *subscription* SKUs
ios:
subscriptionGroupId: "123456789"
pricingTiers:
# Subscription
- ref: pro_monthly
type: monthly
productType: subscription
displayName: Monthly
prices: { USD: 9.99 }
ios:
productId: com.example.pro.monthly
# One-time: lifetime unlock (non-consumable)
- ref: pro_lifetime
type: lifetime
productType: non_consumable
displayName: Lifetime
prices: { USD: 49.99 }
ios:
productId: com.example.pro.lifetime
# One-time: credits pack (consumable)
- ref: credits_100
type: lifetime
productType: consumable
displayName: 100 Credits
prices: { USD: 4.99 }
ios:
productId: com.example.credits.100
```
Notes:
* `type: lifetime` + **no** `productType` defaults to `non_consumable` (backwards compatible).
* Consumables are **not restorable** on iOS. If you need purchase history, use `customerInfo.nonSubscriptionTransactions`.
* For consumables (credits/tokens), treat the purchase as a transaction and fulfill/grant the balance server-side. Do not use local storage as your source of truth.
## Understanding Offerings Structure
Before building your paywall, it's important to understand how offerings are structured:
```javascript theme={null}
{
all: {
"offering-id-1": {
identifier: "offering-id-1",
availablePackages: [...],
monthly: {...},
annual: {...}
},
"offering-id-2": {
identifier: "offering-id-2",
availablePackages: [...]
}
},
current: {
// The offering marked as "current" in your dashboard
identifier: "offering-id-1",
availablePackages: [...]
}
}
```
* `offerings.all` is an **object** with offering IDs as keys (not an array) - `offerings.current`
contains the offering marked as current in your a0 dashboard - If no offering is set as current,
`offerings.current` will be `null` - Use `offerings.current` when possible, or access specific
offerings via their ID from `offerings.all`
## packageId vs productId (don’t mix these up)
* packageId = pkg.identifier (what you pass to purchase(packageId))
* productId = pkg.product.identifier (the store purchase ID)
* iOS: StoreKit product identifier (SKU)
* Stripe: Stripe price ID
If you're using monetization.yaml v2, each pricing tier has a stable ref
(pricingTiers\[].ref). The purchases API includes this as pkg.a0\_packageRef,
and purchase() accepts it as an alias (LLM-friendly).
## Building a Paywall
Here's a simple paywall component using the `useA0Purchases` hook:
```javascript theme={null}
import { useA0Purchases } from "a0-purchases";
function PaywallScreen() {
const { isPremium, isLoading, offerings, purchase, restore } = useA0Purchases();
// Get packages from current offering (recommended) or first available offering
const currentOffering = offerings?.current || Object.values(offerings?.all || {})[0];
const packages = currentOffering?.availablePackages || [];
const handlePurchase = async (pkg) => {
const packageId = pkg.identifier; // what purchase() expects
const productId = pkg.product.identifier; // Stripe price ID or iOS SKU
try {
await purchase(packageId);
console.log("Purchased", { packageId, productId });
if (isPremium) {
// Success! Navigate to premium content
}
} catch (error) {
if (!error.userCancelled) {
// Show error to user
}
}
};
if (isLoading) {
return ;
}
if (packages.length === 0) {
return No products available;
}
return (
{packages.map((pkg) => (
handlePurchase(pkg)}>
{pkg.product.title}{pkg.product.priceString}
{pkg.packageType === "WEEKLY"
? "per week"
: pkg.packageType === "MONTHLY"
? "per month"
: pkg.packageType === "ANNUAL"
? "per year"
: ""}
))}
);
}
```
Your paywall will show no products if: - You haven't created any offerings in the a0 dashboard -
Your offering has no packages assigned to it - You're accessing `offerings.current` but haven't
set an offering as current (use the fallback pattern above) - Your packages are not mapped to
provider products (for iOS: StoreKit product IDs / SKUs; for Stripe: price IDs) - You're testing
iOS purchases in the a0 app / Expo Go (use TestFlight / native builds)
## Checking Premium Status
The simplest way to gate content is using the `isPremium` property:
```javascript theme={null}
function PremiumContent() {
const { isPremium } = useA0Purchases();
if (!isPremium) {
return ;
}
return ;
}
```
## Checking Specific Entitlements
`isPremium` returns true if the user has ANY active entitlement. If you have multiple
features/entitlements and need to check specific ones, use `getCustomerInfo()`.
```javascript theme={null}
function FeatureGatedContent() {
const { getCustomerInfo } = useA0Purchases();
const customerInfo = getCustomerInfo();
// Check for specific entitlements
const hasProFeature = customerInfo?.entitlements.active["PRO"];
const hasAdFree = customerInfo?.entitlements.active["AD_FREE"];
const hasUnlimitedStorage = customerInfo?.entitlements.active["UNLIMITED_STORAGE"];
return (
{hasProFeature && }
{hasAdFree ? : }
{hasUnlimitedStorage && }
);
}
```
## Advanced Implementation
### Custom Paywall with Package Details
```javascript theme={null}
function DetailedPaywallScreen() {
const { offerings, purchase, isPremium } = useA0Purchases();
const currentOffering = offerings?.current;
if (!currentOffering) {
return Loading products...;
}
// Get different package types
const weeklyPackage = currentOffering.weekly;
const monthlyPackage = currentOffering.monthly;
const annualPackage = currentOffering.annual;
return (
Choose your plan:
{weeklyPackage && (
purchase(weeklyPackage.identifier)} />
)}
{monthlyPackage && (
purchase(monthlyPackage.identifier)}
highlighted={true} // Most popular
/>
)}
{annualPackage && (
purchase(annualPackage.identifier)}
savings="Save 25%"
/>
)}
);
}
```
### Handling Purchase States
```javascript theme={null}
import { PURCHASES_ERROR_CODE, useA0Purchases } from "a0-purchases";
function PurchaseButton({ packageId }) {
const { purchase } = useA0Purchases();
const [isPurchasing, setIsPurchasing] = useState(false);
const [error, setError] = useState(null);
const handlePurchase = async () => {
setIsPurchasing(true);
setError(null);
try {
const result = await purchase(packageId);
// Success - the hook will update isPremium automatically
navigation.navigate("Success");
} catch (err) {
if (err.userCancelled) {
// User cancelled - no need to show error
} else if (err.code === PURCHASES_ERROR_CODE.PRODUCT_ALREADY_PURCHASED_ERROR) {
// User already has this product
setError("You already have an active subscription");
} else {
// Other error
setError("Purchase failed. Please try again.");
}
} finally {
setIsPurchasing(false);
}
};
return (
<>
{error && {error}}
>
);
}
```
## Testing Purchases
### Test Credentials
**Stripe Test Cards:**
* Success: `4242 4242 4242 4242`
* Decline: `4000 0000 0000 0002`
* More test cards in [Stripe docs](https://stripe.com/docs/testing)
**Apple Sandbox:**
* Sync products to App Store Connect (production), then test purchases via TestFlight using sandbox tester accounts
* Subscriptions auto-renew every few minutes for testing
* Payments do not work in the a0 app or Expo Go (use TestFlight / native builds)
### Testing Flow
For Stripe, ensure you synced to the sandbox environment in your payment setup.
Make test purchases using the test credentials above.
Check that `isPremium` updates correctly and specific entitlements are active.
Clear app data and test the restore purchases functionality.
## Troubleshooting
**Cause**: `offerings.all` is an object (with offering IDs as keys), not an array
**Solution**:
Use one of these patterns instead:
```javascript theme={null}
// Option 1: Use current offering (recommended)
const packages = offerings?.current?.availablePackages || [];
// Option 2: Get first offering from all
const firstOffering = Object.values(offerings?.all || {})[0];
const packages = firstOffering?.availablePackages || [];
// Option 3: Use current with fallback to first available
const currentOffering = offerings?.current || Object.values(offerings?.all || {})[0];
const packages = currentOffering?.availablePackages || [];
```
Debug your offerings structure by logging:
```javascript theme={null}
console.log('Offerings structure:', JSON.stringify(offerings, null, 2));
```
**Common causes**:
* No offerings created or no current offering set
* The offering has packages, but the packages are **not mapped** to provider products for the current platform/environment
**Solution**:
* Go to a0 dashboard → Payments → Offerings
* Create an offering with your subscription plans
* Set it as the current offering
If you already have offerings/packages configured, make sure the packages are mapped to provider products:
* **iOS (existing products)**: use `.a0/monetization.yaml` v2 linking
* `bundles[].ios.subscriptionGroupId`: numeric App Store Connect subscription group id (digits only)
* `bundles[].pricingTiers[].ios.productId`: existing StoreKit product identifier (SKU)
* **Stripe (existing prices)**: use `.a0/monetization.yaml` v2 linking
* `bundles[].pricingTiers[].stripe.priceId`: Stripe live price id (production)
* `bundles[].pricingTiers[].stripe.testPriceId`: Stripe test price id (development)
* **iOS / Stripe (new products)**: run a provider sync after editing monetization so mappings are created
After a successful provider sync, a0 writes the resulting provider IDs back into `.a0/monetization.yaml` (v2) to reduce drift.
**Common causes**:
* Not synced to payment provider
* Testing iOS purchases in the a0 app / Expo Go (must use TestFlight / native build)
* Testing with production credentials
* Invalid product identifiers
**Debug steps**:
```javascript theme={null}
const { offerings } = useA0Purchases();
console.log("Available offerings:", offerings);
console.log("Current offering:", offerings?.current);
console.log("Available packages:", offerings?.current?.availablePackages);
```
**Cause**: Customer info not refreshing
**Solution**:
The hook should update automatically, but you can force a refresh:
```javascript theme={null}
const { refreshCustomerInfo } = useA0Purchases();
// Force refresh after purchase
await refreshCustomerInfo();
```
**Cause**: Features not properly configured in a0 dashboard
**Solution**:
* Verify feature names match exactly (case-sensitive)
* Ensure plans include the features you're checking
* Sync plans to payment providers after changes
## Next Steps
Complete library documentation with all available methods and properties.
Build and test payment flows on physical devices.
# Payment Setup
Source: https://docs.a0.dev/development/monetization/payment-setup
Set up subscription plans and monetize your a0 app
# Setting Up Payments
Add subscription-based monetization to your a0 app with our integrated payment system.
a0 payments currently support:
* **Web**: Stripe (test and production)
* **iOS**: Apple App Store (TestFlight / App Store)
* **Android**: not supported yet
Apple payments do not work in the a0 app or Expo Go. To test iOS purchases, you must use
TestFlight or a native iOS build.
You can manage plans in two ways:
* Use the dashboard UI, or
* Edit the canonical config file: `.a0/monetization.yaml` (this is what the Monetization tab edits).
If you're using the a0 coding agent, it **can** edit `.a0/monetization.yaml` and trigger provider sync. It should not ask you to do the setup manually.
## Overview
The payment system consists of three main components:
1. **Features** - Entitlements users get (e.g., PRO, AD\_FREE, UNLIMITED\_STORAGE)
2. **Subscription Plans** - Pricing tiers that include one or more features
3. **Offerings** - Bundles of plans presented to users (REQUIRED for paywalls)
## Step-by-Step Setup
### 1. Navigate to Payments
In your a0 dashboard, go to your project and click on your **App Name** to access the settings.
Click on the **Payments** tab to access the payment configuration.
### 2. Create Features
Features represent the entitlements or capabilities users get when they subscribe.
Click **Add Feature** and create features like:
* `PRO` - Access to premium content
* `AD_FREE` - Remove all advertisements
* `UNLIMITED_STORAGE` - No storage limits
* `PRIORITY_SUPPORT` - 24/7 support access
Use descriptive names that make it easy to understand what each feature provides.
Features can be reused across multiple subscription plans, so think of them as building blocks.
### 3. Create Subscription Plans
Click **Create Plan** to set up your first subscription tier.
Each plan requires:
* **Plan Name**: e.g., "Pro Plan", "Premium Subscription"
* **At least 1 Feature**: Select from your created features
* **At least 1 Pricing Tier**: Choose from weekly, monthly, or annual
Enter prices for each tier you want to offer:
* **Weekly**: Best for short-term trials
* **Monthly**: Most popular option
* **Annual**: Offer discounts for long-term commitment
### 4. Sync to Payment Providers
**Critical**: Your payments will NOT work until you sync your plans to the appropriate payment provider. This is the most common setup mistake.
Under your created plan, click the **Sync Plan** button.
Select your payment provider:
* **Stripe**: For Web
* **Apple App Store**: For iOS (IPA)
**For Stripe:**
* Toggle "Use Stripe sandbox environment" for testing
* Must link your Stripe account first
* Sync to sandbox for testing, production for live apps (both are supported on web)
**For Apple App Store:**
* Ensure your app is linked with valid Team ID
* Sync creates products in App Store Connect (production). You test purchases via TestFlight using sandbox tester accounts.
* Invalid Team ID will cause sync to fail
Click **Start Sync** to create the products in your payment provider.
Once synced successfully, you'll see a confirmation and your products will be created in Stripe/App Store Connect.
### 5. Create Offerings
**Important**: Offerings are REQUIRED for your paywall to display products. Without offerings, your paywall will be empty.
Click **Create Offering** to bundle plans for your paywall.
* **Name**: e.g., "Standard", "Launch Special", "Black Friday"
* **Description**: Explain what's included
* **Select Plans**: Choose which subscription plans to include
Mark which offering should be presented to users by default. This will be available as `offerings.current` in your code.
## Testing Your Setup
**Stripe Test Cards:**
* Success: `4242 4242 4242 4242`
* Decline: `4000 0000 0000 0002`
**Apple Sandbox:**
* Sync products to App Store Connect (production), then test purchases via TestFlight using sandbox tester accounts
* Subscriptions auto-renew rapidly for testing
* Payments will not work in the a0 app or Expo Go (use TestFlight / native builds)
## Implementation
Ready to integrate payments into your app? See the [Payment Implementation](/development/monetization/payment-implementation) guide for code examples and best practices.
## Troubleshooting
**Most common issue**: No offerings created or no current offering set
Solution:
* Create at least one offering
* Include your subscription plans in the offering
* Set one offering as "current"
* Ensure plans are synced to payment providers
**Most common issue**: Forgot to sync or synced to wrong environment
Solution:
* For Stripe testing: Ensure "Use Stripe sandbox environment" is checked
* For iOS testing: Ensure you're testing via TestFlight or a native iOS build (not the a0 app / Expo Go)
* Always sync after making changes to plans
For IOS:
* **Missing subscription metadata in App Store Connect**
Note: Metadata is REQUIRED for App Store submission but may not always be required for TestFlight or native build testing
Common causes:
* Stripe is not connected to your a0 account
* Using production keys in sandbox mode
* Invalid API keys
Fix: Connect Stripe in Settings → Integrations, then retry sync
Common causes:
* Invalid Team ID in project settings
* App not properly configured in App Store Connect
* Missing agreements in App Store Connect
Fix:
* Verify Team ID and ensure app is set up in App Store Connect
* Go to App Store Connect → Your App → Subscriptions
* Fill out all required metadata fields (description, screenshots, etc.)
Remember: You must sync every time you:
* Change pricing
* Add/remove features
* Modify plan details
Changes won't take effect until synced!
## Next Steps
Learn how to implement payments in your app with code examples.
Build and test payment flows on real devices.
# Push Notifications
Source: https://docs.a0.dev/development/push-notifications
Send push notifications to iOS and Android devices from your Convex backend
## Push Notifications
a0 provides built-in push notification delivery through FCM (Android) and APNs (iOS). Your Convex backend sends notifications via a0's API — no third-party push services needed.
Push notifications do NOT work in Expo Go or the a0 preview app.
Test with a custom APK (Android) or TestFlight build (iOS).
## How it works
1. Your app collects device push tokens using `expo-notifications`
2. Your Convex backend calls `internal.notifications.sendNotification`
3. a0 detects the platform from the token and delivers via FCM or APNs
The `convex/notifications.ts` file is automatically included in your project. It's an **internal action** — only your server-side Convex functions can call it, not the client.
## Getting a device push token
Use `getDevicePushTokenAsync()` from `expo-notifications` — **not** `getExpoPushTokenAsync()`:
```ts theme={null}
import * as Notifications from "expo-notifications";
async function registerForPushNotifications() {
const { status } = await Notifications.requestPermissionsAsync();
if (status !== "granted") return null;
const { data: token } = await Notifications.getDevicePushTokenAsync();
return token; // Save this to your Convex database
}
```
## Sending notifications
Call `sendNotification` from your Convex backend using `internal` (not `api`):
```ts theme={null}
import { internal } from "./_generated/api";
// From a mutation — schedule it since mutations can't call actions directly
await ctx.scheduler.runAfter(0, internal.notifications.sendNotification, {
to: devicePushToken,
title: "New Message",
body: "You have a new message",
data: { screen: "chat", chatId: "abc123" },
});
// From an action — call directly
await ctx.runAction(internal.notifications.sendNotification, {
to: [token1, token2, token3],
title: "Announcement",
body: "Check out our new feature!",
badge: 1,
});
```
| Argument | Type | Required | Description |
| -------- | ----------------------------------------------------- | -------- | -------------------------- |
| `to` | `string \| string[]` | Yes | Device push token(s) |
| `title` | `string` | Yes | Notification title |
| `body` | `string` | Yes | Notification body |
| `data` | `Record` | No | Custom data payload |
| `badge` | `number` | No | App icon badge count (iOS) |
## Android Setup
Follow the [Android FCM Setup guide](/development/android-fcm-setup) to configure Firebase Cloud Messaging credentials:
1. Create a [Firebase project](https://console.firebase.google.com/) and add your Android app
2. [Download `google-services.json`](/development/android-fcm-setup#step-2-download-google-servicesjson)
3. [Create a Firebase service account key](/development/android-fcm-setup#step-3-create-a-firebase-service-account-key) (for FCM v1 delivery)
4. [Upload both](/development/android-fcm-setup#step-4-upload-to-a0) in **Project Settings → Build Settings → Android Credentials**
5. Create a new APK build
## iOS Setup
iOS push notifications are configured per Apple Developer team:
1. Connect your Apple Developer account via the **Publish → App Store** flow
2. Set up APNs credentials (automatic or manual) from the team management dialog
3. Create a new App Store / TestFlight build
The `aps-environment` entitlement is included automatically in all builds.
## Handling notifications in your app
```ts theme={null}
import * as Notifications from "expo-notifications";
Notifications.setNotificationHandler({
handleNotification: async () => ({
shouldPlaySound: true,
shouldSetBadge: true,
shouldShowBanner: true,
shouldShowList: true,
}),
});
// When user taps a notification
Notifications.addNotificationResponseReceivedListener((response) => {
const data = response.notification.request.content.data;
// Navigate based on data
});
```
## Troubleshooting
| Issue | Fix |
| -------------------------------------------- | ------------------------------------------------------------------ |
| "no valid aps-environment entitlement" | Create a new build — older builds may not include this entitlement |
| "BadDeviceToken" | Device token expired or from wrong environment — get a fresh token |
| "Firebase service account is not configured" | Upload credentials in Build Settings → Android Credentials |
| "APNs credentials are not configured" | Set up APNs key in Publish → App Store → team management |
| Notifications not received | Must test on TestFlight (iOS) or custom APK (Android), not Expo Go |
# iOS Device Registration
Source: https://docs.a0.dev/development/testing/device-registration
Register your iOS device for testing native builds
# iOS Device Registration
Documentation for iOS device registration is coming soon. This feature allows you to register your iPhone or iPad for testing IPA files directly without TestFlight.
## Coming Soon
We're preparing comprehensive documentation for:
* How to find your device UDID
* Registering devices in your a0 dashboard
* Managing multiple test devices
## In the Meantime
For iOS testing, you can:
1. **Use the a0 App** - Test most features instantly (except payments)
2. **Use TestFlight** - Upload your IPA to App Store Connect for beta testing
3. **Contact Support** - Reach out on Discord for device registration help
Android devices don't require registration - you can install APK files directly after enabling "Unknown sources" in your device settings.
## Next Steps
Test without building using the a0 app or Expo Go.
Learn about building and testing IPA/APK files.
# Mobile App Testing
Source: https://docs.a0.dev/development/testing/mobile-app-testing
Test your app on real devices using the a0 app or Expo Go
## Getting Started
Click **View on your Phone** in your a0 project to generate a QR code that works with:
**iOS Testing**
* Opens directly if installed
* Shows install option if not
* Hot reloading enabled
**Android Testing**
* Must use version 52
* Same QR code works
* Full hot reload support
Once scanned, the connection persists across different apps. You won't need to rescan unless you refresh the page.
## Testing with the a0 App (iOS)
### Quick Setup
Click **View on your Phone** in your a0 project.
* If you have a0 app → Opens immediately
* If not → Shows App Store install link
Your app loads with hot reloading enabled. Save changes in the editor and they appear instantly.
## Testing with Expo Go v52 (Android)
### Setup
Download from the [Expo Website](https://expo.dev/go?sdkVersion=52\&platform=android\&device=false) and verify version 52 in app settings.
Use the same QR code from **View on your Phone** - it works for both a0 app and Expo Go.
App loads with full hot reloading support.
## Limitations
**Important**: The a0 app does NOT support:
* ❌ Payment processing (UI shows but transactions fail)
* ❌ Ad networks
* ❌ Some custom native modules
For testing payments or ads, you must [test a native build](/development/testing/native-build-testing).
## Hot Reloading
Both a0 app and Expo Go provide seamless hot reloading:
1. **Instant Updates**: Save in editor → See changes immediately
2. **Persistent Connection**: Switch between apps without rescanning
3. **State Preservation**: Component state maintained during updates
4. **Error Recovery**: Clear errors shown with stack traces
The hot reload connection stays active even when switching between different apps on your phone. Only a page refresh breaks the connection.
## Development Workflow
### Rapid Testing Cycle
```mermaid theme={null}
flowchart LR
A[Edit Code] --> B[Save File]
B --> C[Auto Updates]
C --> D[Test on Device]
D --> A
```
### Testing Multiple Devices
You can test on multiple devices simultaneously:
* Each device scans the same QR code
* All devices update together
* Test iOS and Android side-by-side
## Debugging
### Developer Menu
Shake your device or tap the screen with three fingers to access:
* **Reload** - Refresh the app
* **Debug** - Connect to debugger
* **Inspector** - View component tree
* **Perf Monitor** - See performance metrics
### Console Logs
```javascript theme={null}
// Logs appear in:
// 1. Browser console (if connected)
// 2. Device logs
console.log('Debug message');
console.warn('Warning');
console.error('Error details');
```
## Troubleshooting
* Ensure that all your code is correct, this usually happens when there is an error
* For Android, verify Expo Go is v52
* Try refreshing and generating new QR
* Open the project directly within the app instead of with the QR
* Check error messages in console
* Verify all imports are correct
* Some web-only code may not work
* Review device logs for details
This is expected behavior:
* a0 app and Expo Go can't process payments
* UI will show but transactions fail
* Build IPA/APK for payment testing
## Next Steps
Ready to test payments or ads? Build an IPA or APK with full native support.
Learn how to push updates to all your users instantly.
# Native Build Testing
Source: https://docs.a0.dev/development/testing/native-build-testing
Build and test IPA/APK files with full feature support including payments
## Building Your App
### Generate Builds
a0 provides a simple button to generate both IPA and APK files:
1. Log in to your [a0 Dashboard](https://a0.dev/projects)
2. Open a chat in your selected project
3. click the **Deployment** button in the top right.
Click the "New Update" button to ensure your deployed code is up to date
1. Click on **iPhone** or **Android** icon in the modal
2. Click on **Create a IPA/APK**
3. Once generated, download the file to test on your device
### iOS (IPA) Requirements
iOS apps require device registration before installation. Documentation for device registration coming soon.
For now, iOS testing requires:
* Registered device with a0
### Android (APK) Installation
Android is simpler - just download and install:
On your Android device:
* Settings → Security
* Enable "Unknown sources" or "Install unknown apps"
* Download APK via link or QR code
* Tap to install
* Grant requested permissions
## Key Differences from Mobile Testing
| Feature | a0 App / Expo Go | Native Build |
| ------------ | ---------------- | ----------------- |
| Build Time | Instant | \~2 minutes |
| Payments | ❌ UI only | ✅ Full processing |
| Updates | Hot reload | OTA updates |
| Distribution | QR code | Link or QR |
| Testing Ease | Immediate | Download first |
**Speed up your workflow:**
1. Test UI/UX in a0 app first
2. Build native only for payments/production testing
3. Use OTA updates after initial build
## Common Issues
Device registration required:
* iOS device must be registered with a0
* Use TestFlight as alternative
* Verify payment provider sync
* Check product IDs match exactly
* Use correct test credentials
* Review payment logs
* Enable unknown sources
* Check minimum Android version
* Verify APK not corrupted
* Try different browser/method
## Next Steps
Ready to go live? Follow the iOS submission guide.
Update your app instantly without rebuilding.
# Overview
Source: https://docs.a0.dev/development/testing/overview
a0 provides multiple testing methods to ensure your app works perfectly before deployment.
## Testing Methods Comparison
| Method | Platforms | Features | Limitations | Best For |
| ----------------- | ----------- | ------------------------------- | ----------------------------------- | ----------------- |
| **Web Preview** | Web | Instant updates, full debugging | No native features | Rapid development |
| **a0 Mobile App** | iOS | Quick testing, no build needed | No payments, iOS only | UI/UX testing |
| **Expo Go** | Android | Standard React Native features | Limited native modules, no payments | Android testing |
| **Native Builds** | iOS/Android | Full features, payments work | Requires build time, no hot reload | Final testing |
## Quick Start
Test instantly in your browser with hot reload and debugging tools.
Use the a0 app for iOS or Expo Go for Android testing.
Build and test native IPA/APK files with all features enabled.
Register iOS devices for testing native builds.
# Web Preview Testing
Source: https://docs.a0.dev/development/testing/web-preview
Test your app instantly in the built-in preview with react-native-web
# Web Preview Testing
Every a0 project includes a live preview that updates instantly as you code.
The web preview uses react-native-web to show your app directly in the browser, providing the fastest development feedback loop.
## How It Works
The preview panel appears next to your chat conversation, showing your app in real-time:
* **Instant Updates**: Changes appear immediately as you save
* **Console Logs**: View debug output directly in your browser console
* **Responsive Testing**: Test different screen sizes
## Using Web Preview
### Viewing Your App
Your app preview loads automatically when you open a project. The preview runs a react-native-web version of your app, giving you immediate visual feedback.
On desktop, look for the refresh icon above the preview to manually reload if needed.
### Debugging with Console
```javascript theme={null}
// All console methods work in web preview
console.log('Debug message');
console.error('Error details');
console.table(data);
console.time('Operation');
// ... your code
console.timeEnd('Operation');
```
### Testing on Mobile Devices
Find the **View on your Phone** button in your a0 interface.
A QR code appears with instructions:
* If you have the a0 app → Opens directly
* If not → Shows install option
* Also works with Expo Go v52
Your app loads on your phone with hot reloading enabled. Changes sync automatically without rescanning.
The connection persists across different apps - no need to rescan unless you refresh the page.
## Limitations
* ❌ Native device features (camera, biometrics)
* ❌ Push notifications
* ❌ Native modules
* ❌ Platform-specific components
* ⚠️ Some React Native libraries may not be web-compatible
The web preview shows a react-native-web version of your app. Some features that work on mobile may behave differently or not work in the web preview.
## Troubleshooting
* Click the refresh icon above the preview
* Hard refresh the page if needed
* Check console for errors
React Native Web may render some styles differently:
* Test critical layouts on actual devices
* Use the mobile preview for accurate representation
* Consider Platform-specific styles when needed
## Next Steps
Use the QR code to test on real devices with the a0 app or Expo Go.
Need to test payments or native features? Build an IPA or APK.
# Introduction
Source: https://docs.a0.dev/index
Welcome to the a0.dev Docs
## Prompting Guide
Learn how to use the chat and get the most out of a0
Follow our three step quickstart guide.
## Agent Guide
Learn how to effectively communicate with a0.dev to get the best results
Discover patterns and best practices for talking to a0.dev
## Publishing
Publish your apps to app stores and distribute them to users
Publish to the App Store with a0.dev
## Advanced
Advanced topics for managing and maintaining your a0 projects
Learn why and how to upgrade your a0 project to maintain compatibility
## Start Making Apps
Visit the a0.dev website to start building apps now.
# Automatic Setup
Source: https://docs.a0.dev/publishing/ios/app-store-connect/automatic-setup
Automatically Setup your App Store Connect Account with a0.dev
The Automatic Setup modal will guide you through connecting your account to [a0.dev](http://a0.dev). This is the recommended way to connect your account.
## Requirements
* Acces to [A paid Apple Developer Account](https://developer.apple.com/programs/enroll/)
* Admin Persissions or higher inside an Apple Development Team
## Troubleshooting
### Make sure Team API Keys are enabled for your account
* See [https://developer.apple.com/documentation/AppStoreConnectAPI/creating-api-keys-for-app-store-connect-api](https://developer.apple.com/documentation/AppStoreConnectAPI/creating-api-keys-for-app-store-connect-api) for more information on Team API Keys.
* The account owner may need to enable this feature by going to [https://appstoreconnect.apple.com/access/integrations/api](https://appstoreconnect.apple.com/access/integrations/api)
### Check your Account Permissions
* You'll need Admin Permissions or higher inside your Apple Developer Account to connect it to a0.dev.
* If you already have Admin Permissions and the connection is failing the account owner may have to remove and re-set your Admin permissions.
### I still can't connect my account
If you're unable to connect your account using the automatic setup, you'll have to [Connect your Account Manually](/publishing/ios/app-store-connect/manual-setup)
# Manual Setup
Source: https://docs.a0.dev/publishing/ios/app-store-connect/manual-setup
Manually connect your App Store Connect Account
Manual Setup requires manually creating a Team API Key in App Store Connect and uploading the values to [a0.dev](http://a0.dev)
## Requirements
* Acces to [A paid Apple Developer Account](https://developer.apple.com/programs/enroll/)
* Admin Persissions or higher inside an Apple Development Team
### Step 1: Creating The Key
You'll need to create a Team API Key for a0 with Admin Permissions and save this file to your computer.
Visit [https://appstoreconnect.apple.com/access/integrations/api](https://appstoreconnect.apple.com/access/integrations/api) and select the "Team Keys" tab under "App Store Connect API"
Press the Blue Plus button
* Give your new Key a name like "a0 API Key"
* Make sure to set the access to **Admin**
* Press the blue "Generate" button
* Press the Download button on the right of the row that contains your new key.
* Save the .p8 to a secure location on your computer. You'll only be able to download this file once.
### Step 2: Uploading the Key to [a0](http://a0.dev)
* Hover over the Key in App Store Connect and press Copy Key Id
The issuer ID is in the middle of the Team Keys Page
1. Navigate to [Your Apple Developer Profile](https://developer.apple.com/account#:~:text=Team%20ID)
2. Scroll down to the Membership Details Section
3. Copy your Team ID
The Team Name is used to identify your team in a0.
1. Navigate to [Your Apple Developer Profile](https://developer.apple.com/account)
2. Scroll down to the Membership Details Section
3. Copy the value next to **Entity Name** (for organizations) or your name (for individual accounts)
This is the same page where you found your Team ID.
* The Private Key is a .p8 file saved to your computer from the previous step.
* Make sure that the name of the private key matches the Key ID you enter in a0.
# Setup your App Store Connect Account
Source: https://docs.a0.dev/publishing/ios/app-store-connect/setup
How to connect your App Store Connect Account to a0.dev
## Requirements
* Acces to [A paid Apple Developer Account](https://developer.apple.com/programs/enroll/)
* Admin Persissions or higher inside an Apple Development Team
## Where do I connect my account?
1. Visit [https://a0.dev/settings/integrations](https://a0.dev/settings/integrations)
2. Click Manage under App Store Connect
3. Press "Add API Key"
4. Choose your setup method:
* [Automatic Setup](/publishing/ios/app-store-connect/automatic-setup) - Recommended for most users
* [Manual Setup](/publishing/ios/app-store-connect/manual-setup) - Manually upload an existing key
##
# Apple Developer Program
Source: https://docs.a0.dev/publishing/ios/apple-developer-program
What the Apple Developer Program is, how to enroll, and how to connect it to a0.dev
## What is the Apple Developer Program?
The [Apple Developer Program](https://developer.apple.com/programs/) is Apple's membership for distributing apps on the App Store. It costs **\$99/year** and gives you access to:
* Publishing apps to the App Store and TestFlight
* App Store Connect for managing your app listings, builds, and submissions
* Code signing certificates and provisioning profiles
* Access to beta versions of iOS, macOS, and other Apple platforms
You need an active Apple Developer Program membership to upload your app to the App Store through a0.dev.
## How to enroll
If you don't already have one, create an Apple ID at [appleid.apple.com](https://appleid.apple.com/).
1. Visit [developer.apple.com/programs/enroll](https://developer.apple.com/programs/enroll/)
2. Click **Start Your Enrollment**
3. Sign in with your Apple ID
4. Choose your entity type:
* **Individual** — if you're publishing as yourself
* **Organization** — if you're publishing as a company (requires a D-U-N-S number)
5. Complete payment (\$99/year)
Organization enrollments require a [D-U-N-S number](https://developer.apple.com/support/D-U-N-S/) and may take additional time to verify. Individual enrollments are typically approved within 48 hours.
Apple reviews all enrollments. Individual accounts are usually approved within 48 hours. Organization accounts may take longer due to additional verification.
## Connect your account to a0.dev
Once your Apple Developer Program membership is active:
1. Open your project on [a0.dev](https://a0.dev)
2. Click **Publish** and select **App Store**
3. Click **Connect** on the Developer Account step
4. Follow the guided setup to connect your App Store Connect API key
For detailed instructions on connecting your account, see [Setup your App Store Connect Account](/publishing/ios/app-store-connect/setup).
## Helpful links
* [Apple Developer Program enrollment](https://developer.apple.com/programs/enroll/)
* [Apple Developer Program overview](https://developer.apple.com/programs/)
* [App Store Connect](https://appstoreconnect.apple.com/)
* [Check enrollment status](https://developer.apple.com/account/)
# Deploying Your App
Source: https://docs.a0.dev/publishing/ios/deploying-on-a0
Step-by-step guide to deploy your iOS app using the a0 platform
This guide walks you through the process of deploying your iOS app using the a0 platform. We'll cover everything from initial setup to generating your App Store-ready build.
## Prerequisites
* [Apple Developer Program Connected to a0](/publishing/ios/app-store-connect/setup)
* [App Store Connect account with Bundle ID and App created](/publishing/ios/preparing-asc-account)
* Your app code ready for deployment
## Accessing the Deployment Dashboard
1. Log in to your [a0 Dashboard](https://a0.dev)
2. Open a chat in your selected project
3. click the **Deployment** button in the top right.
1. If you haven't already, click the "New Update" button to ensure your deployed code is up to date
2. Click on **App Store** icon in the modal then click on **Create a Build**
3. You'll see the deployment configuration interface
The interface should show your project's current deployment status.
## Configuring Your iOS Build
If you haven't already connected your account, follow the steps [here](/publishing/ios/app-store-connect/setup)
This step only needs to be completed once per account.
1. Confirm your app version, name and icon look correct (this can be modified in settings)
2. From the BundleID dropdown select the app you created in the previous step
3. From the Team ID dropdown, select the Team ID which is associated with the selected Bundle ID
If the Bundle ID isn't within the same account as the Team ID your build will not work
1. Click the Generate & Submit button at the bottom of the deployment modal
2. Wait until the loading modal is complete and your app should appear within your app store connect account.
It can take up to 10 minutes for your build to appear on your account
## Post-Build Actions
After your build is generated and submitted, follow these verification steps:
* Check that the build appears in App Store Connect
* Navigate to **TestFlight** to see your build
* Verify build number and version match
## Testing Your Build
Before submitting to the App Store:
1. Add internal testers in App Store Connect
2. Create a test group
3. Distribute the build to testers
4. Collect feedback and iterate
1. Submit for Beta App Review (if required)
2. Add external testers
3. Monitor usage analytics
4. Address any reported issues
## Next Steps
With your app successfully deployed:
1. Complete [pre-submission checklist](/publishing/ios/submission-checklist)
2. Submit for App Store review
3. Monitor review status and respond to feedback
Join our Discord for real-time support with deployment issues.
##
# Preparing your App Store Connect Account
Source: https://docs.a0.dev/publishing/ios/preparing-asc-account
Learn how to set up your App Store Connect account for publishing, including creating Bundle IDs and app entries
Before you can publish your iOS app, you need to ensure your App Store Connect account is properly configured. This guide walks you through creating the necessary Bundle ID and app entry if you don't already have them.
## Prerequisites
You must have completed the [App Store Connect setup](/publishing/ios/app-store-connect/setup) before proceeding with this guide.
* Active Apple Developer Program membership
* Access to App Store Connect with Admin or App Manager role
* Your app's basic information (name, description, category)
## Creating a Bundle ID
A Bundle ID uniquely identifies your app across Apple's ecosystem. If you haven't created one yet, follow these steps:
1. Log in to [Apple Developer Portal](https://developer.apple.com/account)
2. Navigate to **Certificates, Identifiers & Profiles**
3. Select **Identifiers** from the sidebar
1. Click the **+** button to create a new identifier
2. Select **App IDs** and click **Continue**
3. Choose **App** as the type and click **Continue**
Fill in the required information:
* **Description**: A human-readable name for your app
* **Bundle ID**: Use reverse domain notation (e.g., `com.yourcompany.appname`)
Choose your Bundle ID carefully - it cannot be changed after submission to the App Store.
Use a consistent naming convention like `com.yourcompany.appname` to maintain organization.
Choose the capabilities your app requires:
* Push Notifications
* In-App Purchase
* Game Center
* Apple Pay
* And others as needed
Only select capabilities your app actually uses to avoid unnecessary configuration.
1. Review your configuration
2. Click **Continue** and then **Register**
3. Your Bundle ID is now ready for use
## Creating Your App in App Store Connect
If you haven't created your app entry in App Store Connect, follow these steps:
1. Log in to [App Store Connect](https://appstoreconnect.apple.com)
2. Click **My Apps**
3. Click the **+** button and select **New App**
Fill in the required fields:
Select **iOS** for your mobile app
Your app's name as it will appear on the App Store (max 30 characters)
The primary language for your app's metadata
Select the Bundle ID you created in the previous section
A unique identifier for your app (not visible to users)
1. Review your information
2. Click **Create**
3. Your app is now created in App Store Connect
You should see your new app in the My Apps section.
## Configuring App Information
Once your app is created, you'll need to complete several sections:
* **Category**: Select the primary and secondary categories
* **Content Rights**: Specify if your app contains third-party content
* **Age Rating**: Complete the questionnaire to determine your app's rating
Consider rating your app 17+ to avoid content-related rejections during App Store review.
* **Price**: Set your app's price or mark it as free
* **Availability**: Choose which countries/regions to distribute in
* **App Store Distribution**: Configure how your app appears in search results
## Verification Checklist
Before proceeding to deployment, ensure you have:
* [ ] Created a Bundle ID with appropriate capabilities
* [ ] Created your app in App Store Connect
* [ ] Completed basic app information
* [ ] Set pricing and availability
* [ ] Configured age rating
These settings can be modified later, but having them configured early streamlines the deployment process.
## Next Steps
With your App Store Connect account prepared, you're ready to:
1. [Deploy your build](/publishing/ios/deploying-on-a0)
2. [Complete the submission checklist](/publishing/ios/submission-checklist)
3. Submit for App Store review
Join our Discord community for support with App Store Connect configuration.
# Pre-Submission Checklist
Source: https://docs.a0.dev/publishing/ios/submission-checklist
Complete checklist to ensure your iOS app is ready for App Store submission and review
Before submitting your iOS app to the App Store, use this comprehensive checklist to ensure your app meets all requirements and has the best chance of approval.
## Quick Start for a0 Apps
1. **Rate your app 17+** - Significantly reduces rejection risk
2. **Configure privacy tracking** - Set "Device ID" in both "Data Used to Track You" and "Data Not Linked to You"
3. **Use a0's privacy URL** - [https://a0.dev/privacy](https://a0.dev/privacy) for immediate compliance
4. **Verify Bundle ID match** - Must be identical in a0 dashboard and App Store Connect
5. **Test on real devices** - Ensure your app works flawlessly before submission
* **Privacy labels incorrect** - Most common issue, double-check Device ID configuration
* **Bundle ID mismatch** - Ensure exact match between platforms
* **Age rating too restrictive** - Consider 17+ even for clean content
* **Screenshots don't match app** - Use actual app screenshots, not mockups
## App Store Connect Configuration
### Basic App Information
Ensure all required fields are completed:
* [ ] **App Name**: Clear, descriptive, and follows App Store guidelines
* [ ] **Subtitle**: Concise description of your app's purpose (30 characters max)
* [ ] **Category**: Primary and secondary categories accurately reflect your app
* [ ] **Keywords**: Relevant keywords for App Store search (100 characters max)
* [ ] **Description**: Compelling description highlighting key features
Use all available characters in your description to maximize visibility and conversions.
Verify all required visual assets are uploaded:
* [ ] **iPhone Screenshots**: All required screen sizes and orientations
* [ ] **App Icon**: High-resolution app icon (1024x1024)
Complete the privacy questionnaire accurately:
* [ ] **Privacy Policy**: URL to your privacy policy
* [ ] **Data Collection**: Accurately describe what data you collect
* [ ] **Data Usage**: Explain how collected data is used
* [ ] **Third-party SDKs**: Disclose any third-party data collection
* [ ] **Data Used to Track You**: Configure "Identifiers > Device ID" (a0 uses this to optimize your app)
* [ ] **Data Not Linked to You**: Also configure "Identifiers > Device ID" if applicable
Privacy configuration must be 100% accurate. Incorrect privacy labels are a common cause of App Store rejection.
**Quick start**: Use [https://a0.dev/privacy](https://a0.dev/privacy) as your privacy URL to get started immediately. We're working on providing personalized privacy policies in the future.
### Pricing and Distribution
* [ ] **Pricing**: Set appropriate price or mark as free
* [ ] **Availability**: Selected target countries/regions
* [ ] **Release Options**: Choose manual or automatic release after approval
* [ ] **Educational Discount**: Configure if offering educational pricing
### Legal Compliance
* [ ] **Terms of Service**: Available and easily accessible
* [ ] **Privacy Policy**: Use [https://a0.dev/privacy](https://a0.dev/privacy) or ensure your own policy covers Device ID collection
* [ ] **COPPA Compliance**: If targeting children under 13 (not recommended - use 17+ instead)
* [ ] **Regional Laws**: Compliance with laws in target markets
* [ ] **Data Protection**: GDPR, CCPA compliance if applicable
## Testing and Quality Assurance
### Functional Testing
* [ ] All primary features work correctly
* [ ] User flows complete successfully
* [ ] Data persistence works properly
* [ ] Account creation and login (if applicable)
* [ ] In-app purchases function correctly (if applicable)
* [ ] Poor network connectivity scenarios
* [ ] Low storage space conditions
* [ ] Interruptions (calls, notifications)
* [ ] App backgrounding and foregrounding
* [ ] Device rotation and orientation changes
## Submission Process
Once you've completed this checklist:
1. Log in to App Store Connect
2. Navigate to your app
3. Select the build for submission
4. Click **Submit for Review**
* Review typically takes 24-48 hours
* Check for any messages from the App Review team
* Respond promptly to any requests for information
* App will be available on the App Store (unless set to manual release)
* Monitor user reviews and ratings
* Plan for future updates and improvements
If you encounter issues during submission, our Discord community is here to help.
# Deploying Updates
Source: https://docs.a0.dev/publishing/ota-updates/deploy-updates
Step-by-step guide to push Over the Air Updates to your app
## How To Update
1. Log in to your [a0 Dashboard](https://a0.dev/projects)
2. Open a chat in your selected project
3. click the **Deployment** button in the top right.
Click the "New Update" button to ensure your deployed code is up to date
The changes should reflect in your app immediately, simply close your app from the background and reopen it.
## Next Steps
Learn how to thoroughly test your app before deploying updates.
Ready to publish your app to the App Store? Start here.
# Update Overview
Source: https://docs.a0.dev/publishing/ota-updates/overview
Deploy updates to all platforms instantly without rebuilding your app
# How It Works
a0's Over the Air Updates system allows you to deploy your latest code to all platforms simultaneously. This revolutionary approach means you can:
* Fix bugs instantly without App Store review times
* Add features without forcing users to update
* Test changes in production quickly
* Maintain one codebase for all platforms
This technology works similarly to React Native's CodePush or Expo Updates, but it's built directly into a0 and works across all platforms — not just mobile.
## Next Steps
Learn how to push your first Over the Air Update to all platforms.
Test your updates before deploying to production.