Expo SDK Integration Guide
This guide provides comprehensive instructions for integrating the Passage Expo SDK into your React Native/Expo application and implementing the required backend infrastructure.
Table of Contents
Installation
Requirements
- Expo SDK 48+
- React Native 0.70+
- Node.js 16+
Expo Installation
Add the package to your Expo project:
npx expo install @getpassage/expo
SDK Integration
Overview
To integrate the Passage Expo SDK, you need to complete these steps:
- Configure SDK → Initialize once in your app
- Backend Setup → Create intent token endpoint
- Get Token → Call your backend for intent token
- Open Passage → Launch connection flow with callbacks
- Handle Results → Process success/error responses
Quick Reference
Step 1: Configure SDK
import { configure, PassageConfig } from '@getpassage/expo';
// Configure once (typically in App.tsx)
const config: PassageConfig = { debug: true };
configure(config);
Step 2: Get Intent Token
// Call your backend to get intent token
const response = await fetch('https://your-api.com/intent-token');
const { intentToken } = await response.json();
Step 3: Open Passage Flow
import { open, PassageSuccessData, PassageErrorData } from '@getpassage/expo';
await open({
token: intentToken,
onConnectionComplete: (data: PassageSuccessData) => {
console.log('Success:', data.connectionId);
},
onConnectionError: (error: PassageErrorData) => {
console.log('Error:', error.error);
}
});
Detailed Implementation
Complete Expo Integration
import React, { useState } from 'react';
import { View, Text, TouchableOpacity, Alert } from 'react-native';
import { configure, open, PassageSuccessData, PassageErrorData, PassageDataResult } from '@getpassage/expo';
// Configure SDK at app startup
configure({ debug: true });
export default function App() {
const [isLoading, setIsLoading] = useState(false);
const [result, setResult] = useState('');
const connectAccount = async () => {
setIsLoading(true);
try {
// 1. Get intent token from your backend
const intentToken = await getIntentToken();
// 2. Open Passage with callbacks
await open({
token: intentToken,
onConnectionComplete: (data: PassageSuccessData) => {
setResult(`✅ Connected! ID: ${data.connectionId}`);
},
onConnectionError: (error: PassageErrorData) => {
setResult(`❌ Error: ${error.error}`);
},
onDataComplete: (data: PassageDataResult) => {
console.log('History items:', data.data?.history?.length || 0);
// Process the captured data
if (data.data?.history) {
data.data.history.forEach(item => {
if (item.structuredData) {
console.log('Data:', item.structuredData);
}
});
}
},
onExit: (reason) => {
console.log('User exited:', reason);
}
});
} catch (error) {
setResult(`Error: ${error.message}`);
} finally {
setIsLoading(false);
}
};
const getIntentToken = async (): Promise<string> => {
const response = await fetch('https://your-api.com/api/intent-token');
const data: { intentToken: string } = await response.json();
return data.intentToken;
};
return (
<View style={{ flex: 1, justifyContent: 'center', padding: 20 }}>
<TouchableOpacity
onPress={connectAccount}
disabled={isLoading}
style={{
backgroundColor: isLoading ? '#ccc' : '#007AFF',
padding: 15,
borderRadius: 8,
marginBottom: 20
}}
>
<Text style={{ color: 'white', textAlign: 'center', fontSize: 16 }}>
{isLoading ? 'Connecting...' : 'Connect Account'}
</Text>
</TouchableOpacity>
{result ? (
<Text style={{ fontSize: 14, textAlign: 'center' }}>
{result}
</Text>
) : null}
</View>
);
}
Using React Context (Alternative)
import React from 'react';
import { PassageProvider, usePassage, PassageSuccessData, PassageErrorData } from '@getpassage/expo';
export default function App() {
return (
<PassageProvider config={{ debug: true }}>
<MyComponent />
</PassageProvider>
);
}
function MyComponent() {
const { open } = usePassage();
const handleConnect = async () => {
const intentToken: string = await getIntentToken();
await open({
token: intentToken,
onConnectionComplete: (data: PassageSuccessData) => {
console.log('Success:', data.connectionId);
},
onConnectionError: (error: PassageErrorData) => {
console.error('Error:', error.error);
}
});
};
// ... rest of component
}
Backend Implementation
Your backend needs to create intent tokens for the Expo app to use. Here's a typical implementation:
Node.js Example
app.post('/api/intent-token', async (req, res) => {
try {
// 1. Get access token using OAuth 2.0 Client Credentials
const accessToken = await getPassageAccessToken();
// 2. Create intent token
const response = await fetch('https://api.getpassage.ai/intent-token', {
method: 'POST',
headers: {
'Authorization': `Bearer ${accessToken}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
integrationId: 'netflix', // or other integrations
resources: {
genericHistory: {
read: {}
}
}
})
});
const { intentToken } = await response.json();
res.json({ intentToken });
} catch (error) {
res.status(500).json({ error: error.message });
}
});
async function getPassageAccessToken() {
const response = await fetch('https://api.getpassage.ai/oauth/token', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
grant_type: 'client_credentials',
client_id: process.env.PASSAGE_CLIENT_ID,
client_secret: process.env.PASSAGE_CLIENT_SECRET
})
});
const { access_token } = await response.json();
return access_token;
}
API Reference
Configuration
import { configure } from '@getpassage/expo';
interface PassageConfig {
debug?: boolean; // Enable debug logging (default: false)
baseUrl?: string; // Custom base URL (optional)
socketUrl?: string; // Custom socket URL (optional)
}
configure(config: PassageConfig): void;
Core Methods
import { open, close } from '@getpassage/expo';
// Open connection flow
await open({
token: string;
onConnectionComplete?: (data: PassageSuccessData) => void;
onConnectionError?: (error: PassageErrorData) => void;
onDataComplete?: (data: PassageDataResult) => void;
onExit?: (reason?: string) => void;
});
// Close connection programmatically
close(): void;
Data Types
PassageSuccessData
interface PassageSuccessData {
history: PassageHistoryItem[]; // Captured data items
connectionId: string; // Unique connection identifier
}
interface PassageHistoryItem {
vendorId: string; // Unique identifier for this item
structuredData?: any; // Integration-specific data (see Integration Data Types)
rawData?: any; // Original raw data from the service
enrichedData?: any; // Additional enriched data (where available)
}
📚 For detailed data structures returned by each integration, see Integration Data Types →
PassageErrorData
interface PassageErrorData {
error: string; // Error message
data?: any; // Additional error data
}
PassageDataResult
interface PassageDataResult {
data?: any; // Raw data payload
prompts?: Record<string, any>[]; // Associated prompts
}
Performance
The Passage Expo SDK is designed to be lightweight with minimal impact on your app's performance.
Benchmarking Results
Performance measurements conducted on iPhone 15 Pro with iOS 17 using real-time monitoring during typical SDK usage:
Kroger.com (Complex E-commerce Website)
| Metric | Baseline | With Passage SDK | Overhead | Maximum Spikes |
|---|---|---|---|---|
| Memory | 9.4 MB | 23.3 MB | +13.9 MB | 25.5 MB during page load |
| CPU | 0.0% | 0.0% avg | Minimal | 18.4% during page load |
| App Startup | N/A | N/A | No impact | N/A |
Audible.com (Media/Content Website)
| Metric | Baseline | With Passage SDK | Overhead | Maximum Spikes |
|---|---|---|---|---|
| Memory | 8.9 MB | 23.9 MB | +15.0 MB | 25.1 MB during page load |
| CPU | 0.0% | 0.0% avg | Minimal | 7.1% during page load |
| App Startup | N/A | N/A | No impact | N/A |
Performance Characteristics
- Memory Usage: ~14-15 MB overhead for full SDK functionality including WebView rendering
- CPU Impact: Minimal sustained usage with brief spikes during web page loading operations
- Complex e-commerce sites: Spikes up to 18.4% during heavy page loads
- Media content sites: Spikes up to 7.1% during typical page operations
- Web Page Complexity: Performance scales with website complexity and JavaScript usage
- UI Responsiveness: Maintains smooth 120/60+ FPS during normal usage
- Startup Time: No measurable impact on app launch performance
- Memory Management: WebView instances are automatically released after sessions to free memory while preserving cookies/localStorage for user convenience
Integration Data Types
- Browse integrations and resources in the explorer
Support
If you encounter issues:
- GitHub Issues: Create an issue with logs and steps to reproduce
- Email Support: support@getpassage.ai
- Documentation: Latest updates at docs.getpassage.ai