← Back to Guides

Mobile App Integration

Add license validation to your React Native, Flutter, or native mobile app.

1. Quick Start

Call our API from your app. Here's the simplest version:

const response = await fetch('https://api.unifiedlicensing.com/api/v1/validate-license', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
        api_key: 'ul_YOUR_API_KEY',
        product_key: 'YOUR_PRODUCT_KEY',
        license_key: 'PLAYER-LICENSE-KEY',
        machine_id: 'device-unique-id',
        platform: 'mobile'
    })
});
const result = await response.json();
if (result.valid) { /* unlock features */ }

2. Setup

Get your API Key (Settings tab, starts with ul_) and Product Key (Products tab) from your vendor dashboard.

Mobile apps call the API directly. The API key identifies your vendor account, not a specific user. It's safe to ship in your app.

3. React Native

import React, { useState, useEffect } from 'react';
import { View, Text, TextInput, TouchableOpacity, ActivityIndicator } from 'react-native';
import AsyncStorage from '@react-native-async-storage/async-storage';
import DeviceInfo from 'react-native-device-info';

const API = 'https://api.unifiedlicensing.com/api/v1';

export default function LicenseScreen({ onLicensed }) {
    const [key, setKey] = useState('');
    const [loading, setLoading] = useState(true);
    const [error, setError] = useState('');

    useEffect(() => { checkSaved(); }, []);

    async function checkSaved() {
        const saved = await AsyncStorage.getItem('license_key');
        if (saved) {
            const r = await validate(saved);
            if (r.valid) { onLicensed(); return; }
        }
        setLoading(false);
    }

    async function validate(licenseKey) {
        const id = await DeviceInfo.getUniqueId();
        const res = await fetch(`${API}/validate-license`, {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({
                api_key: 'ul_YOUR_API_KEY',
                product_key: 'YOUR_PRODUCT_KEY',
                license_key: licenseKey,
                machine_id: id,
                platform: 'mobile'
            })
        });
        return res.json();
    }

    async function activate() {
        setLoading(true); setError('');
        const r = await validate(key.trim());
        if (r.valid) {
            await AsyncStorage.setItem('license_key', key.trim());
            onLicensed();
        } else {
            setError(r.error || 'Invalid license');
        }
        setLoading(false);
    }

    if (loading) return ;
    return (
        <View style={{flex:1,justifyContent:'center',padding:20,backgroundColor:'#081220'}}>
            <Text style={{color:'#C7A34C',fontSize:24,textAlign:'center',marginBottom:20}}>Enter License Key</Text>
            <TextInput value={key} onChangeText={setKey} placeholder="XXXX-XXXX-XXXX"
                style={{borderWidth:1,borderColor:'#C7A34C',padding:12,borderRadius:8,color:'#EFEAE0',marginBottom:12}} />
            <TouchableOpacity onPress={activate} style={{backgroundColor:'#C7A34C',padding:14,borderRadius:8,alignItems:'center'}}>
                <Text style={{color:'#081220',fontWeight:'bold',fontSize:16}}>Activate</Text>
            </TouchableOpacity>
            {error ? <Text style={{color:'#B5502E',marginTop:10,textAlign:'center'}}>{error}</Text> : null}
        </View>
    );
}

4. Flutter

import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:shared_preferences/shared_preferences.dart';
import 'package:device_info_plus/device_info_plus.dart';

class LicenseService {
  static const _api = 'https://api.unifiedlicensing.com/api/v1';
  static const _apiKey = 'ul_YOUR_API_KEY';
  static const _productKey = 'YOUR_PRODUCT_KEY';

  static Future<bool> validate(String licenseKey) async {
    final deviceInfo = DeviceInfoPlugin();
    final androidInfo = await deviceInfo.androidInfo;
    final machineId = androidInfo.id;

    final res = await http.post(
      Uri.parse('$_api/validate-license'),
      headers: {'Content-Type': 'application/json'},
      body: jsonEncode({
        'api_key': _apiKey, 'product_key': _productKey,
        'license_key': licenseKey, 'machine_id': machineId, 'platform': 'mobile'
      }),
    );
    final data = jsonDecode(res.body);
    if (data['valid'] == true) {
      final prefs = await SharedPreferences.getInstance();
      await prefs.setString('license_key', licenseKey);
    }
    return data['valid'] == true;
  }

  static Future<bool> checkSaved() async {
    final prefs = await SharedPreferences.getInstance();
    final saved = prefs.getString('license_key');
    if (saved != null) return validate(saved);
    return false;
  }
}

5. Android (Kotlin)

// In your Activity
suspend fun validateLicense(key: String): Boolean = withContext(Dispatchers.IO) {
    val androidId = Settings.Secure.getString(contentResolver, Settings.Secure.ANDROID_ID)
    val body = JSONObject().apply {
        put("api_key", "ul_YOUR_API_KEY")
        put("product_key", "YOUR_PRODUCT_KEY")
        put("license_key", key)
        put("machine_id", androidId)
        put("platform", "mobile")
    }
    val req = Request.Builder()
        .url("https://api.unifiedlicensing.com/api/v1/validate-license")
        .post(body.toString().toRequestBody("application/json".toMediaType()))
        .build()
    val resp = OkHttpClient().newCall(req).execute()
    val json = JSONObject(resp.body?.string() ?: "{}")
    if (json.optBoolean("valid", false)) {
        getSharedPreferences("app", MODE_PRIVATE).edit().putString("license_key", key).apply()
    }
    json.optBoolean("valid", false)
}

6. iOS (Swift)

func validateLicense(_ key: String) async throws -> Bool {
    let deviceID = UIDevice.current.identifierForVendor?.uuidString ?? "unknown"
    var req = URLRequest(url: URL(string: "https://api.unifiedlicensing.com/api/v1/validate-license")!)
    req.httpMethod = "POST"
    req.addValue("application/json", forHTTPHeaderField: "Content-Type")
    req.httpBody = try JSONEncoder().encode([
        "api_key": "ul_YOUR_API_KEY",
        "product_key": "YOUR_PRODUCT_KEY",
        "license_key": key,
        "machine_id": deviceID,
        "platform": "mobile"
    ])
    let (data, _) = try await URLSession.shared.data(for: req)
    let json = try JSONSerialization.jsonObject(with: data) as? [String: Any]
    let valid = json?["valid"] as? Bool ?? false
    if valid { UserDefaults.standard.set(key, forKey: "license_key") }
    return valid
}

7. Offline Support

Cache the last successful validation. On next launch, check cache first (instant), then re-validate in background.

8. Trial Support

// Start trial
const res = await fetch('https://api.unifiedlicensing.com/api/v1/start-trial', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
        api_key: 'ul_YOUR_API_KEY', product_key: 'YOUR_PRODUCT_KEY',
        machine_id: machineId, platform: 'mobile'
    })
});
const data = await res.json();
// data.trial_token, data.remaining_days
await AsyncStorage.setItem('trial_token', data.trial_token);

// Check trial on app start
const check = await fetch('https://api.unifiedlicensing.com/api/v1/check-trial', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
        api_key: 'ul_YOUR_API_KEY', product_key: 'YOUR_PRODUCT_KEY',
        trial_token: savedToken, machine_id: machineId, platform: 'mobile'
    })
});

9. Device Fingerprinting

PlatformIdentifierAPI
AndroidANDROID_IDSettings.Secure.ANDROID_ID
iOSidentifierForVendorUIDevice.current.identifierForVendor
React NativeDevice unique IDreact-native-device-info
FlutterDevice IDdevice_info_plus

10. App Store Rules

11. Troubleshooting

ProblemFix
Network errorCheck your API URL includes https:// and the full path /api/v1/validate-license
"Invalid license" for valid keyCheck api_key and product_key match your dashboard
Works on iOS but not AndroidCheck Android network security config. Ensure cleartext is not required (use HTTPS).
License valid but features lockedYour app isn't reading the validation result correctly. Check the response parsing.
App rejected by AppleMake sure you're not mentioning external purchases in your app description