React Native SDK ⚛️
Build secure cross-platform mobile apps with hardware-backed device attestation and 20+ AI providers. One codebase - iOS AND Android protected!
How it works: Every API request is cryptographically verified using Apple’s App Attest (iOS) or Google’s Play Integrity (Android). This proves the request came from YOUR legitimate app on a real device - not a hacker, bot, or compromised device. Your API keys never touch the device.
Requirements
| Requirement | Details |
|---|---|
| React Native 0.70+ | Modern React Native version |
| iOS 14.0+ | App Attest support |
| Android API 24+ | Play Integrity support |
| Physical Device | Simulators/emulators have limited attestation |
| ProtectMyAPI Account | Sign up free |
Testing: Test on real devices! iOS Simulator doesn’t support App Attest, and Android Emulator has limited Play Integrity support.
Installation
Add the Package
# Using npm
npm install @protectmyapi/react-native-sdk
# Using yarn
yarn add @protectmyapi/react-native-sdk
# Using pnpm
pnpm add @protectmyapi/react-native-sdkiOS Setup
- Install pods:
cd ios && pod install- Open your project in Xcode
- Select your app target → Signing & Capabilities
- Click + Capability → Add App Attest
Ensure your Bundle ID matches what’s configured in the ProtectMyAPI Dashboard.
Android Setup
- Go to Google Cloud Console
- Enable Play Integrity API
- In ProtectMyAPI Dashboard, add:
- Your Package Name (e.g.,
com.yourcompany.yourapp) - Your SHA-256 fingerprint
- Your Package Name (e.g.,
# Find your SHA-256
cd android && ./gradlew signingReportUpdate your android/app/build.gradle:
android {
defaultConfig {
minSdkVersion 24 // Required for Play Integrity
}
}Initialize the SDK
import { ProtectMyAPI } from '@protectmyapi/react-native-sdk';
// In your app entry point (App.tsx or index.js)
async function initializeApp() {
await ProtectMyAPI.initialize({
appToken: 'app_your_token_here',
environment: 'production',
enableAttestation: true,
enableSecurityChecks: true,
});
}
// Call before your app renders
initializeApp().then(() => {
// App is ready
});Configuration Options
The SDK offers extensive configuration:
import { ProtectMyAPI } from '@protectmyapi/react-native-sdk';
await ProtectMyAPI.initialize({
// Required
appToken: 'app_your_token_here',
// Environment
environment: 'production', // 'development' | 'staging' | 'production'
// Security Options
enableAttestation: true, // Enable App Attest / Play Integrity
enableSecurityChecks: true, // Run security checks
allowEmulator: false, // Block emulators/simulators
allowRootedDevices: false, // Block rooted/jailbroken devices
enableCertificatePinning: true, // Pin TLS certificates
certificatePins: [ // Custom certificate hashes
'sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA='
],
// Network Options
timeout: 30000, // Request timeout in ms
debugMode: false, // Enable debug logging
// Retry Configuration
retryConfig: {
maxRetryAttempts: 3, // Auto-retry failed requests
initialRetryDelayMs: 1000, // Initial retry delay
maxRetryDelayMs: 30000, // Max retry delay
retryBackoffMultiplier: 2.0, // Exponential backoff
},
});Configuration Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
appToken | string | Required | Your app token from the dashboard |
environment | string | 'production' | 'development', 'staging', or 'production' |
enableAttestation | boolean | true | Enable device attestation |
enableSecurityChecks | boolean | true | Run jailbreak/root detection |
allowEmulator | boolean | false | Allow emulators/simulators |
allowRootedDevices | boolean | false | Allow rooted/jailbroken devices |
enableCertificatePinning | boolean | false | Pin TLS certificates |
certificatePins | string[] | [] | Custom certificate pin hashes |
timeout | number | 30000 | Network timeout in milliseconds |
debugMode | boolean | false | Enable debug logging |
Retry Configuration
| Parameter | Type | Default | Description |
|---|---|---|---|
maxRetryAttempts | number | 3 | Maximum retry attempts |
initialRetryDelayMs | number | 1000 | Initial delay in milliseconds |
maxRetryDelayMs | number | 30000 | Maximum delay in milliseconds |
retryBackoffMultiplier | number | 2.0 | Backoff multiplier |
Making Secure Requests
Basic Requests
import { ProtectMyAPI } from '@protectmyapi/react-native-sdk';
// GET request
const users = await ProtectMyAPI.get('/api/users');
// POST request
const newUser = await ProtectMyAPI.post('/api/users', {
name: 'John Doe',
email: '[email protected]',
});
// PUT request
const updated = await ProtectMyAPI.put('/api/users/123', {
name: 'John Updated',
});
// PATCH request
const patched = await ProtectMyAPI.patch('/api/users/123', {
email: '[email protected]',
});
// DELETE request
await ProtectMyAPI.delete('/api/users/123');Full Request Options
const response = await ProtectMyAPI.secureRequest({
endpoint: '/api/data',
method: 'POST',
body: {
key: 'value',
},
headers: {
'X-Custom-Header': 'custom-value',
},
});TypeScript Support
interface User {
id: string;
name: string;
email: string;
}
// Typed responses
const user = await ProtectMyAPI.get<User>('/api/users/123');
console.log(user.name); // TypeScript knows this is a string
const users = await ProtectMyAPI.get<User[]>('/api/users');
users.forEach(u => console.log(u.email));React Hooks
The SDK provides React hooks for seamless integration:
useProtectMyAPI
Manage SDK lifecycle and initialization:
import { useProtectMyAPI } from '@protectmyapi/react-native-sdk';
function App() {
const {
isInitialized,
isInitializing,
error,
initialize,
securityReport,
refreshSecurityReport,
} = useProtectMyAPI();
useEffect(() => {
if (!isInitialized && !isInitializing) {
initialize({
appToken: 'app_your_token_here',
environment: __DEV__ ? 'development' : 'production',
allowEmulator: __DEV__,
});
}
}, [isInitialized, isInitializing]);
if (isInitializing) {
return <LoadingScreen />;
}
if (error) {
return <ErrorScreen error={error} />;
}
return <MainApp />;
}useSecureRequest
Make API requests with loading and error states:
import { useSecureRequest } from '@protectmyapi/react-native-sdk';
interface UserProfile {
id: string;
name: string;
avatar: string;
}
function ProfileScreen() {
const {
data,
loading,
error,
execute,
reset,
} = useSecureRequest<UserProfile>('/api/user/profile');
useEffect(() => {
execute();
}, []);
if (loading) {
return <ActivityIndicator />;
}
if (error) {
return (
<View>
<Text>Error: {error.message}</Text>
<Button title="Retry" onPress={() => execute()} />
</View>
);
}
return (
<View>
<Image source={{ uri: data?.avatar }} />
<Text>{data?.name}</Text>
</View>
);
}useSecurityReport
Monitor device security status:
import { useSecurityReport } from '@protectmyapi/react-native-sdk';
function SecurityStatusBadge() {
const {
report,
isSecure,
loading,
refresh,
} = useSecurityReport({
refreshOnForeground: true,
refreshInterval: 60000, // Check every minute
allowEmulator: __DEV__,
allowRootedDevices: false,
});
if (!isSecure) {
return (
<View style={styles.warning}>
<Text>⚠️ Security Issue Detected</Text>
{report?.isRooted && <Text>Device is rooted/jailbroken</Text>}
{report?.isEmulator && <Text>Running on emulator</Text>}
{report?.isHooked && <Text>Hooking framework detected</Text>}
</View>
);
}
return (
<View style={styles.secure}>
<Text>✅ Device Secure</Text>
</View>
);
}Security Features
Security Report
import { ProtectMyAPI } from '@protectmyapi/react-native-sdk';
const report = await ProtectMyAPI.getSecurityReport();
console.log('Rooted/Jailbroken:', report.isRooted);
console.log('Emulator:', report.isEmulator);
console.log('Hooks detected:', report.isHooked);
console.log('Debugger attached:', report.isDebuggerAttached);
console.log('App tampered:', report.isAppTampered);
console.log('Reverse engineered:', report.isReverseEngineered);
console.log('Failed checks:', report.failedChecks);Manual Security Checks
import { SecurityChecker } from '@protectmyapi/react-native-sdk';
// Individual checks
const isRooted = await SecurityChecker.isDeviceRooted();
const isEmulator = await SecurityChecker.isEmulator();
const isHooked = await SecurityChecker.isHookingDetected();
const isDebugged = await SecurityChecker.isDebuggerAttached();
const isTampered = await SecurityChecker.isAppTampered();
// Block if compromised
if (isRooted || isHooked) {
Alert.alert(
'Security Alert',
'This app cannot run on compromised devices.',
[{ text: 'OK', onPress: () => BackHandler.exitApp() }]
);
}Enforce Security Checks
import { SecurityChecker } from '@protectmyapi/react-native-sdk';
try {
await SecurityChecker.enforceSecurityChecks({
allowEmulator: __DEV__,
allowRootedDevices: false,
});
// All checks passed
} catch (error) {
// Security violation detected
console.error('Security violation:', error.message);
}What’s Detected
| Check | Description |
|---|---|
| Jailbreak | Cydia, Sileo, checkra1n, unc0ver, Substitute |
| Hooking | Frida, Cycript, Substrate, LibHook |
| Debugger | lldb, gdb attached debuggers |
| Simulator | iOS Simulator detection |
| Tampering | Code signature validation, binary modifications |
| Reverse Engineering | Disassembly tools, runtime introspection |
Error Handling
Error Types
import {
ProtectMyAPI,
ProtectMyAPIError,
ErrorCode
} from '@protectmyapi/react-native-sdk';
try {
const data = await ProtectMyAPI.get('/api/data');
} catch (error) {
if (error instanceof ProtectMyAPIError) {
switch (error.code) {
case ErrorCode.NOT_INITIALIZED:
console.error('SDK not initialized');
break;
case ErrorCode.ATTESTATION_FAILED:
console.error('Device attestation failed');
console.error('Reason:', error.integrityReason);
break;
case ErrorCode.SECURITY_VIOLATION:
console.error('Security violation:', error.violationType);
// e.g., 'ROOTED_DEVICE', 'HOOK_DETECTED', 'EMULATOR_DETECTED'
break;
case ErrorCode.NETWORK_ERROR:
console.error('Network error:', error.message);
break;
case ErrorCode.SERVER_ERROR:
console.error(`Server error (${error.statusCode}):`, error.message);
break;
case ErrorCode.RATE_LIMITED:
console.error('Rate limit exceeded');
break;
case ErrorCode.UNAUTHORIZED:
console.error('Invalid app token');
break;
default:
console.error('Unknown error:', error.message);
}
// Check if error is retryable
if (error.isRetryable()) {
// Implement retry logic
}
}
}Error Codes Reference
| Code | Description |
|---|---|
NOT_INITIALIZED | SDK not initialized |
INVALID_CONFIGURATION | Invalid configuration |
ATTESTATION_FAILED | Device attestation failed |
ATTESTATION_NOT_SUPPORTED | Attestation not supported on device |
SECURITY_VIOLATION | Security check failed |
NETWORK_ERROR | Network connectivity issue |
SERVER_ERROR | Server returned an error |
RATE_LIMITED | Rate limit exceeded |
UNAUTHORIZED | Invalid or expired app token |
INVALID_RESPONSE | Invalid response from server |
CERTIFICATE_PINNING_FAILED | Certificate pinning validation failed |
Complete Example
Here’s a full example of a React Native app using the SDK:
import React, { useEffect, useState } from 'react';
import {
SafeAreaView,
View,
Text,
FlatList,
TouchableOpacity,
ActivityIndicator,
StyleSheet,
Alert,
} from 'react-native';
import {
ProtectMyAPI,
useProtectMyAPI,
useSecureRequest,
useSecurityReport,
ProtectMyAPIError,
ErrorCode,
} from '@protectmyapi/react-native-sdk';
// Types
interface Post {
id: string;
title: string;
body: string;
}
// App Component
export default function App() {
const { isInitialized, isInitializing, error, initialize } = useProtectMyAPI();
const { isSecure, report } = useSecurityReport({ allowEmulator: __DEV__ });
useEffect(() => {
initialize({
appToken: 'app_your_token_here',
environment: __DEV__ ? 'development' : 'production',
enableAttestation: true,
enableSecurityChecks: true,
allowEmulator: __DEV__,
debugMode: __DEV__,
});
}, []);
if (isInitializing) {
return (
<SafeAreaView style={styles.container}>
<ActivityIndicator size="large" />
<Text>Initializing...</Text>
</SafeAreaView>
);
}
if (error) {
return (
<SafeAreaView style={styles.container}>
<Text style={styles.error}>Error: {error.message}</Text>
</SafeAreaView>
);
}
if (!isSecure) {
return (
<SafeAreaView style={styles.container}>
<Text style={styles.error}>⚠️ Security Issue Detected</Text>
<Text>{report?.failedChecks.join(', ')}</Text>
</SafeAreaView>
);
}
return (
<SafeAreaView style={styles.container}>
<PostsList />
</SafeAreaView>
);
}
// Posts List Component
function PostsList() {
const { data: posts, loading, error, execute } = useSecureRequest<Post[]>('/api/posts');
const [creating, setCreating] = useState(false);
useEffect(() => {
execute();
}, []);
const createPost = async () => {
setCreating(true);
try {
await ProtectMyAPI.post('/api/posts', {
title: 'New Post',
body: 'This is a secure post!',
});
execute(); // Refresh list
Alert.alert('Success', 'Post created!');
} catch (err) {
if (err instanceof ProtectMyAPIError) {
Alert.alert('Error', err.message);
}
} finally {
setCreating(false);
}
};
if (loading) {
return <ActivityIndicator size="large" />;
}
if (error) {
return (
<View>
<Text style={styles.error}>{error.message}</Text>
<TouchableOpacity onPress={() => execute()}>
<Text>Retry</Text>
</TouchableOpacity>
</View>
);
}
return (
<View style={styles.flex}>
<TouchableOpacity
style={styles.button}
onPress={createPost}
disabled={creating}
>
<Text style={styles.buttonText}>
{creating ? 'Creating...' : 'Create Post'}
</Text>
</TouchableOpacity>
<FlatList
data={posts}
keyExtractor={(item) => item.id}
renderItem={({ item }) => (
<View style={styles.post}>
<Text style={styles.title}>{item.title}</Text>
<Text>{item.body}</Text>
</View>
)}
/>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 16,
backgroundColor: '#fff',
},
flex: {
flex: 1,
},
error: {
color: 'red',
fontSize: 16,
textAlign: 'center',
},
button: {
backgroundColor: '#007AFF',
padding: 12,
borderRadius: 8,
marginBottom: 16,
},
buttonText: {
color: '#fff',
textAlign: 'center',
fontWeight: '600',
},
post: {
padding: 16,
borderBottomWidth: 1,
borderBottomColor: '#eee',
},
title: {
fontSize: 18,
fontWeight: '600',
marginBottom: 8,
},
});AI Integration
The SDK works seamlessly with all ProtectMyAPI AI providers:
// OpenAI Chat
const response = await ProtectMyAPI.post('/openai/chat', {
message: 'Hello, how are you?',
model: 'gpt-4o-mini',
});
// Anthropic Claude
const response = await ProtectMyAPI.post('/anthropic/chat', {
message: 'Explain quantum computing',
model: 'claude-3-5-sonnet-latest',
});
// Generate Images with DALL-E
const image = await ProtectMyAPI.post('/openai/images', {
prompt: 'A sunset over mountains',
size: '1024x1024',
});
// Transcribe Audio
const formData = new FormData();
formData.append('file', audioFile);
const transcription = await ProtectMyAPI.post('/openai/transcribe', formData);See the AI Providers section for complete documentation on all 20+ supported AI providers.
Troubleshooting
iOS: “App Attest not available”
- Ensure you’re running on a physical device (not simulator)
- Verify the App Attest capability is enabled in Xcode
- Check that your Bundle ID matches the ProtectMyAPI Dashboard
Android: “Play Integrity unavailable”
- Ensure you have Google Play Services installed
- The device must pass basic integrity checks
- For testing, use a signed release build
”Security violation detected”
This occurs when security checks detect a compromised environment:
// For development only:
await ProtectMyAPI.initialize({
appToken: 'your-token',
allowEmulator: __DEV__, // Allow in development
allowRootedDevices: false, // Never allow in production
debugMode: __DEV__,
});Never disable security checks in production builds!
Network Errors
The SDK includes automatic retry with exponential backoff:
await ProtectMyAPI.initialize({
appToken: 'your-token',
retryConfig: {
maxRetryAttempts: 5, // Increase retries
initialRetryDelayMs: 2000, // Start with 2s delay
maxRetryDelayMs: 60000, // Max 60s delay
retryBackoffMultiplier: 2, // Double each time
},
});Best Practices
Configure the SDK in your app’s entry point before making any API calls.
🔐 Initialize EarlyAlways wrap API calls in try-catch blocks and handle all error types.
⚠️ Handle ErrorsUse environment variables or secure configuration - never hardcode tokens.
🔑 Secure TokensAlways test on physical devices for full attestation support.
📱 Test on DevicesKeep security checks enabled in production builds.
🛡️ Enable SecurityLog and monitor security violations in production.
📊 Monitor ReportsMigration from Other SDKs
From iOS SDK
The React Native SDK follows similar patterns:
// iOS Swift
ProtectMyAPI.shared.configure(appId: "token")
let data = try await ProtectMyAPI.shared.get(endpoint: "/api/data")// React Native
await ProtectMyAPI.initialize({ appToken: 'token' });
const data = await ProtectMyAPI.get('/api/data');From Android SDK
// Android Kotlin
ProtectMyAPI.initialize(context, config)
val data = ProtectMyAPI.get("/api/data")// React Native
await ProtectMyAPI.initialize({ appToken: 'token' });
const data = await ProtectMyAPI.get('/api/data');From Flutter SDK
// Flutter Dart
await ProtectMyAPI.initialize(appId: 'token');
final data = await ProtectMyAPI.instance.get('/api/data');// React Native
await ProtectMyAPI.initialize({ appToken: 'token' });
const data = await ProtectMyAPI.get('/api/data');