AI
Pass
Docs>SDK>React Native

React Native SDK

Build powerful mobile applications with OAuth authentication, AI API calls, and React hooks - optimized for Expo.

React NativeExpo CompatibleOAuth2 + PKCEv1.1.0

Step 1: Get Your Client ID

You need a Client ID to use the SDK. Get one for free in 30 seconds:

  1. Go to AI Pass Developer Panel
  2. Sign up or log in
  3. Click "Create OAuth App"
  4. Copy your Client ID
Get Client ID Now
Tip: Your Client ID looks like: aipass_xxxxxxxxxxxx

5-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-app

2. Install dependencies

npx expo install expo-auth-session expo-crypto expo-linking expo-secure-store expo-web-browser @react-native-async-storage/async-storage buffer

3. Download the SDK

# Create lib folder and download SDK
mkdir -p lib
curl -o lib/AiPassSDK.js https://aipass.one/docs/sdk/reactnative/AiPassSDK.js

4. 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 start
Important: Replace YOUR_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.js

Overview

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-system
Note: The SDK is designed for Expo projects. For bare React Native, additional configuration may be required.

Installation

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:

OptionTypeDefaultDescription
clientIdstringrequiredYour AI Pass client ID
baseUrlstring'https://aipass.one'API base URL
scopesstring[]['api:access', 'profile:read']OAuth scopes to request
storageKeystring'aipass_oauth_token'Key for token storage
tokenRefreshBuffernumber300000 (5 min)Refresh tokens this many ms before expiry
enableBackgroundRefreshbooleantrueEnable automatic token refresh
debugbooleanfalseEnable 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 instance
  • isReady - Whether SDK is initialized
  • isAuthenticated - Whether user is logged in
  • balance - Current user balance
  • error - Initialization error (if any)
  • login() - Start OAuth login flow
  • logout() - Logout and revoke token
  • refreshBalance() - Manually refresh balance
  • openDashboard() - Open AI Pass dashboard in browser
  • showPaymentModal() - Emit payment required event
  • getTokenInfo() - 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

Make sure your 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/skill

Two 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