React Native SDK
Build powerful mobile applications with OAuth authentication, AI API calls, and React hooks - optimized for Expo.
Step 1: Get Your Client ID
You need a Client ID to use the SDK. Get one for free in 30 seconds:
- Go to AI Pass Developer Panel
- Sign up or log in
- Click "Create OAuth App"
- Copy your Client ID
aipass_xxxxxxxxxxxx5-Minute Quick Start
Get a working AI chat app in 5 minutes. Copy and paste these steps:
1. Create new Expo project
npx create-expo-app@latest my-ai-app
cd my-ai-app2. Install dependencies
npx expo install expo-auth-session expo-crypto expo-linking expo-secure-store expo-web-browser @react-native-async-storage/async-storage buffer3. Download the SDK
# Create lib folder and download SDK
mkdir -p lib
curl -o lib/AiPassSDK.js https://aipass.one/docs/sdk/reactnative/AiPassSDK.js4. Configure app.json
Add scheme to your app.json:
{
"expo": {
"scheme": "myaiapp"
}
}5. Replace App.js with this complete example
import React, { useState } from 'react';
import {
View, Text, TextInput, Button,
FlatList, ActivityIndicator, StyleSheet, SafeAreaView
} from 'react-native';
import { AiPassProvider, useAiPass, useAuthState, useBalance, useAiPassEvent } from './lib/AiPassSDK';
// ============================================
// REPLACE WITH YOUR CLIENT ID FROM STEP 1
// ============================================
const CLIENT_ID = 'YOUR_CLIENT_ID_HERE';
function ChatScreen() {
const { sdk, login, logout, openDashboard } = useAiPass();
const { isReady, isAuthenticated } = useAuthState();
const { balance } = useBalance();
const [messages, setMessages] = useState([]);
const [input, setInput] = useState('');
const [loading, setLoading] = useState(false);
// Handle budget exceeded
useAiPassEvent('budgetExceeded', ({ message }) => {
alert('Budget exceeded: ' + message);
});
// Loading state
if (!isReady) {
return (
<View style={styles.center}>
<ActivityIndicator size="large" color="#4F46E5" />
<Text style={styles.loadingText}>Loading AI Pass...</Text>
</View>
);
}
// Login screen
if (!isAuthenticated) {
return (
<View style={styles.center}>
<Text style={styles.title}>AI Chat</Text>
<Text style={styles.subtitle}>Powered by AI Pass</Text>
<Button title="Sign in with AI Pass" onPress={login} color="#4F46E5" />
</View>
);
}
// Send message
const sendMessage = async () => {
if (!input.trim() || loading) return;
const userMessage = { role: 'user', content: input };
setMessages(prev => [...prev, userMessage]);
setInput('');
setLoading(true);
try {
const result = await sdk.generateCompletion({
messages: [...messages, userMessage],
model: 'gemini/gemini-2.5-flash-lite',
maxTokens: 1000
});
const aiMessage = result.choices[0].message;
setMessages(prev => [...prev, aiMessage]);
} catch (error) {
alert('Error: ' + error.message);
setMessages(prev => prev.slice(0, -1));
} finally {
setLoading(false);
}
};
// Chat screen
return (
<SafeAreaView style={styles.container}>
<View style={styles.header}>
<Text style={styles.balance}>
Balance: ${balance?.toFixed(2) || '...'}
</Text>
<View style={styles.headerButtons}>
<Button title="Dashboard" onPress={openDashboard} />
<Button title="Logout" onPress={logout} color="#ff4444" />
</View>
</View>
<FlatList
data={messages}
keyExtractor={(_, i) => i.toString()}
style={styles.messageList}
renderItem={({ item }) => (
<View style={[
styles.message,
item.role === 'user' ? styles.userMsg : styles.aiMsg
]}>
<Text style={styles.messageText}>{item.content}</Text>
</View>
)}
ListEmptyComponent={
<Text style={styles.emptyText}>Send a message to start chatting!</Text>
}
/>
<View style={styles.inputRow}>
<TextInput
style={styles.input}
value={input}
onChangeText={setInput}
placeholder="Type a message..."
editable={!loading}
onSubmitEditing={sendMessage}
/>
<Button
title={loading ? '...' : 'Send'}
onPress={sendMessage}
disabled={loading || !input.trim()}
color="#4F46E5"
/>
</View>
</SafeAreaView>
);
}
export default function App() {
return (
<AiPassProvider config={{ clientId: CLIENT_ID, debug: __DEV__ }}>
<ChatScreen />
</AiPassProvider>
);
}
const styles = StyleSheet.create({
container: { flex: 1, backgroundColor: '#fff' },
center: { flex: 1, justifyContent: 'center', alignItems: 'center', padding: 20 },
title: { fontSize: 32, fontWeight: 'bold', color: '#4F46E5', marginBottom: 8 },
subtitle: { fontSize: 16, color: '#666', marginBottom: 24 },
loadingText: { marginTop: 12, color: '#666' },
header: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', padding: 16, borderBottomWidth: 1, borderBottomColor: '#eee' },
headerButtons: { flexDirection: 'row', gap: 8 },
balance: { fontSize: 16, fontWeight: '600' },
messageList: { flex: 1, padding: 16 },
message: { padding: 12, borderRadius: 12, marginVertical: 4, maxWidth: '80%' },
userMsg: { backgroundColor: '#4F46E5', alignSelf: 'flex-end' },
aiMsg: { backgroundColor: '#f0f0f0', alignSelf: 'flex-start' },
messageText: { fontSize: 16 },
emptyText: { textAlign: 'center', color: '#999', marginTop: 40 },
inputRow: { flexDirection: 'row', padding: 16, borderTopWidth: 1, borderTopColor: '#eee', alignItems: 'center' },
input: { flex: 1, borderWidth: 1, borderColor: '#ddd', borderRadius: 24, paddingHorizontal: 16, paddingVertical: 12, marginRight: 8, fontSize: 16 }
});6. Run the app
npx expo startYOUR_CLIENT_ID_HERE with your actual Client ID from Step 1!Testing Checklist
- App loads and shows "Sign in with AI Pass" button
- Clicking login opens browser for authentication
- After login, chat screen appears with your balance
- Can send messages and receive AI responses
- Dashboard button opens AI Pass in browser
- Logout returns to login screen
Download SDK
Get the AI Pass React Native SDK for your Expo project:
Download AiPassSDK.jsOverview
The AI Pass React Native SDK provides everything you need to integrate AI capabilities into your mobile application:
OAuth2 + PKCE
Secure browser-based authentication with automatic token management
Secure Storage
Tokens stored with expo-secure-store (AsyncStorage fallback)
Auto Refresh
Automatic token refresh with AppState awareness
AI APIs
Chat, image, video, speech, embeddings and more
React Hooks
useAiPass, useAuthState, useBalance, useChat hooks
Streaming Support
Real-time streaming for chat completions
Prerequisites
Required Expo packages:
# Core dependencies
npx expo install expo-auth-session expo-crypto expo-linking expo-secure-store expo-web-browser @react-native-async-storage/async-storage buffer
# Optional: For audio features (TTS, speech-to-text)
npx expo install expo-av expo-file-systemInstallation
1. Download the SDK
Download AiPassSDK.js and add it to your project (e.g., lib/AiPassSDK.js).
2. Configure Deep Linking
Add the scheme to your app.json:
{
"expo": {
"scheme": "your-app-scheme",
"ios": {
"bundleIdentifier": "com.yourcompany.yourapp"
},
"android": {
"package": "com.yourcompany.yourapp"
}
}
}3. Wrap Your App with Provider
import { AiPassProvider } from './lib/AiPassSDK';
export default function App() {
return (
<AiPassProvider config={{ clientId: 'your_client_id' }}>
<YourApp />
</AiPassProvider>
);
}Quick Start
Basic Usage with Hooks
import React from 'react';
import { View, Button, Text, ActivityIndicator } from 'react-native';
import { useAiPass, useAuthState, useBalance } from './lib/AiPassSDK';
function HomeScreen() {
const { login, logout, sdk } = useAiPass();
const { isReady, isAuthenticated } = useAuthState();
const { balance } = useBalance();
if (!isReady) {
return <ActivityIndicator />;
}
if (!isAuthenticated) {
return (
<View>
<Button title="Sign in with AI Pass" onPress={login} />
</View>
);
}
return (
<View>
<Text>Balance: ${balance?.toFixed(2)}</Text>
<Button title="Generate Text" onPress={async () => {
const result = await sdk.generateCompletion({
prompt: 'Hello, AI!',
model: 'gemini/gemini-2.5-flash-lite'
});
console.log(result.choices[0].message.content);
}} />
<Button title="Logout" onPress={logout} />
</View>
);
}Direct SDK Usage (Without Provider)
import { AiPass } from './lib/AiPassSDK';
// Initialize once at app startup
await AiPass.initialize({ clientId: 'your_client_id' });
// Check authentication
if (!AiPass.isAuthenticated()) {
await AiPass.login();
}
// Make API calls
const result = await AiPass.generateCompletion({
prompt: 'Explain React Native',
model: 'gemini/gemini-2.5-flash-lite',
maxTokens: 500
});Configuration Options
Pass these options to AiPass.initialize() or the AiPassProvider:
| Option | Type | Default | Description |
|---|---|---|---|
| clientId | string | required | Your AI Pass client ID |
| baseUrl | string | 'https://aipass.one' | API base URL |
| scopes | string[] | ['api:access', 'profile:read'] | OAuth scopes to request |
| storageKey | string | 'aipass_oauth_token' | Key for token storage |
| tokenRefreshBuffer | number | 300000 (5 min) | Refresh tokens this many ms before expiry |
| enableBackgroundRefresh | boolean | true | Enable automatic token refresh |
| debug | boolean | false | Enable debug logging |
React Hooks
useAiPass()
Main hook providing access to SDK instance and common actions.
const {
sdk,
isReady,
isAuthenticated,
balance,
error,
login,
logout,
refreshBalance,
openDashboard,
showPaymentModal,
getTokenInfo
} = useAiPass();sdk- The AiPassSDK instanceisReady- Whether SDK is initializedisAuthenticated- Whether user is logged inbalance- Current user balanceerror- Initialization error (if any)login()- Start OAuth login flowlogout()- Logout and revoke tokenrefreshBalance()- Manually refresh balanceopenDashboard()- Open AI Pass dashboard in browsershowPaymentModal()- Emit payment required eventgetTokenInfo()- Get token expiration info
useAuthState()
Hook for authentication state with login/logout actions.
const { isReady, isAuthenticated, error, login, logout } = useAuthState();useBalance()
Hook for tracking user balance.
const { balance, refreshBalance, isAuthenticated, showPaymentModal } = useBalance();useChat()
Hook for chat completion with loading state management.
const { generateCompletion, isLoading, error, isReady, isAuthenticated } = useChat();
// Usage
const handleSend = async () => {
const result = await generateCompletion({
messages: [{ role: 'user', content: 'Hello!' }],
model: 'gemini/gemini-2.5-flash-lite'
});
console.log(result.choices[0].message.content);
};useAiPassEvent()
Hook for subscribing to SDK events.
import { Alert } from 'react-native';
// Subscribe to budget exceeded events
useAiPassEvent('budgetExceeded', (data) => {
Alert.alert('Budget Exceeded', data.message);
});
// Subscribe to payment required events
useAiPassEvent('paymentRequired', ({ balance, dashboardUrl }) => {
navigation.navigate('AddFunds', { balance });
});Troubleshooting
Deep Link Not Working
app.jsonhas the correct scheme configured and you've rebuilt the app after changes.Token Storage Issues
The SDK automatically falls back to AsyncStorage if SecureStore has size limitations (2KB). For large tokens, this is handled automatically.
Expo Go vs Development Build
In Expo Go, deep linking uses exp:// scheme. For production, create a development build with your custom scheme.
Enable Debug Mode
AiPass.initialize({
clientId: 'your_client_id',
debug: true // See detailed logs
});Resources
Using Claude Code, Cursor, or another AI agent?
Drop the AI Pass skill into your agent and skip the manual setup. Works with Claude Code, Codex, Cursor, OpenCode, and 38+ other agents.
npx skills add aipass-one/skillTwo skills available: aipass-api (personal use) and aipass-oauth-app (for app builders).
Stuck? We're happy to help on Discord
Active Discord community with the AI Pass team. Get unblocked on integration, ask about models, share what you're building.
Join AI Pass Discord