Embedded Integration
Embed Vespid in your app using WebView or iframe.
Overview
- Auth Modes: Native or API
- Setup Time: 1-2 hours
- Best For: Seamless in-app experience
User Workflow
- Native Auth
- API Auth (SSO)
- User opens your app
- Your app loads Vespid WebView with partner param
- User creates account or logs in on Vespid
- User is associated with your partner
- User starts learning
- User logs into your app
- Your backend requests SSO token from Vespid API
- Your app loads Vespid WebView with partner param
- Your app sends token to WebView via postMessage
- User is automatically authenticated — no login required
- User starts learning
Prerequisites
- Partner Registration Code — Your unique partner identifier (required for all integrations)
- API Key — Secret key for SSO token generation (API Auth only)
Platform dependencies:
- React Native:
react-native-webview - Cordova:
cordova-plugin-inappbrowser - Android: WebView (built-in)
- iOS: WKWebView (built-in)
tip
Contact your Vespid account manager to obtain your Partner Registration Code and API Key.
Auth Options
| Native Auth | API Auth | |
|---|---|---|
| User creates Vespid account | Yes | No (automatic) |
| Requires API key | No | Yes |
| SSO with your app | No | Yes |
Native Auth
Load Vespid with your partner param. Users authenticate on Vespid.
https://integration.vespid.my?partner=<partner_registration_code>
API Auth
Step 1: Get Token (Backend)
Call the Vespid SSO API from your backend to get a Firebase custom token.
Endpoint
POST https://api.vespid.my/auth/partners/sso/token
Request Headers
| Header | Value | Required |
|---|---|---|
X-API-Key | Partner API key | Yes |
Content-Type | application/json | Yes |
Request Body
| Field | Type | Description | Required |
|---|---|---|---|
email | string | User's email address | One of email or external_user_id required |
external_user_id | string | Your system's unique user identifier | One of email or external_user_id required |
display_name | string | User's display name | No |
// Using email
{
"email": "user@example.com",
"display_name": "John Doe"
}
// Using external_user_id
{
"external_user_id": "usr_12345",
"display_name": "John Doe"
}
Response
{
"success": true,
"data": {
"custom_token": "eyJhbGciOi...",
"expires_at": "2024-01-15T11:30:00Z"
}
}
note
Token is valid for 1 hour. Generate a new token per session.
Step 2: Send Token to WebView
After WebView loads, send the token via postMessage:
{
type: 'AUTH_TOKEN',
payload: { token: 'eyJhbGciOi...' }
}
WebView may request token by sending:
{ type: 'REQUEST_AUTH_TOKEN' }
Platform Setup
- React Native
- Cordova
- Android (Java)
- iOS (Swift)
import React, { useRef, useCallback, useState } from 'react';
import { View, ActivityIndicator, StyleSheet } from 'react-native';
import { WebView, WebViewMessageEvent } from 'react-native-webview';
const VESPID_URL = 'https://integration.vespid.my';
interface Props {
partnerParam: string;
token?: string; // For API auth
}
export const VespidWebView: React.FC<Props> = ({ partnerParam, token }) => {
const webViewRef = useRef<WebView>(null);
const [loading, setLoading] = useState(true);
const tokenSent = useRef(false);
const handleMessage = useCallback((e: WebViewMessageEvent) => {
if (!token) return;
try {
const data = JSON.parse(e.nativeEvent.data);
if (data.type === 'REQUEST_AUTH_TOKEN' && !tokenSent.current) {
tokenSent.current = true;
webViewRef.current?.postMessage(JSON.stringify({
type: 'AUTH_TOKEN',
payload: { token }
}));
}
} catch {}
}, [token]);
return (
<View style={styles.container}>
<WebView
ref={webViewRef}
source={{ uri: `${VESPID_URL}?partner=${partnerParam}` }}
onMessage={handleMessage}
onLoadEnd={() => setLoading(false)}
javaScriptEnabled
domStorageEnabled
/>
{loading && (
<View style={styles.loading}>
<ActivityIndicator size="large" color="#F4B618" />
</View>
)}
</View>
);
};
const styles = StyleSheet.create({
container: { flex: 1 },
loading: { ...StyleSheet.absoluteFillObject, justifyContent: 'center', alignItems: 'center' },
});
Usage:
// Native Auth
<VespidWebView partnerParam="your_company" />
// API Auth
<VespidWebView partnerParam="your_company" token={ssoToken} />
const Vespid = {
URL: 'https://integration.vespid.my',
browser: null,
open(partnerParam, token = null) {
this.browser = cordova.InAppBrowser.open(
`${this.URL}?partner=${partnerParam}`,
'_blank',
'location=no,toolbar=yes,hideurlbar=yes'
);
if (token) {
this.browser.addEventListener('loadstop', () => this.sendToken(token));
this.browser.addEventListener('message', (e) => {
if (e.data?.type === 'REQUEST_AUTH_TOKEN') this.sendToken(token);
});
}
},
sendToken(token) {
this.browser?.executeScript({
code: `window.postMessage(${JSON.stringify({
type: 'AUTH_TOKEN',
payload: { token }
})}, '*');`
});
},
close() {
this.browser?.close();
}
};
// Native Auth
Vespid.open('your_company');
// API Auth
Vespid.open('your_company', ssoToken);
public class VespidWebViewActivity extends AppCompatActivity {
private static final String VESPID_URL = "https://integration.vespid.my";
private WebView webView;
private String token; // For API auth
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_vespid_webview);
String partnerParam = getIntent().getStringExtra("partnerParam");
token = getIntent().getStringExtra("token"); // Optional for API auth
webView = findViewById(R.id.webView);
setupWebView();
webView.loadUrl(VESPID_URL + "?partner=" + partnerParam);
}
private void setupWebView() {
WebSettings settings = webView.getSettings();
settings.setJavaScriptEnabled(true);
settings.setDomStorageEnabled(true);
webView.addJavascriptInterface(new WebAppInterface(), "Android");
webView.setWebViewClient(new WebViewClient() {
@Override
public void onPageFinished(WebView view, String url) {
// Listen for token requests from Vespid
injectMessageListener();
}
});
}
private void injectMessageListener() {
String js = "window.addEventListener('message', function(e) {" +
"if (e.data && e.data.type === 'REQUEST_AUTH_TOKEN') {" +
"Android.onTokenRequested();" +
"}" +
"});";
webView.evaluateJavascript(js, null);
}
private void sendToken() {
if (token == null) return;
String js = String.format(
"window.postMessage({type:'AUTH_TOKEN',payload:{token:'%s'}},'*');",
token
);
webView.evaluateJavascript(js, null);
}
private class WebAppInterface {
@JavascriptInterface
public void onTokenRequested() {
runOnUiThread(() -> sendToken());
}
}
}
Usage:
// Native Auth
Intent intent = new Intent(this, VespidWebViewActivity.class);
intent.putExtra("partnerParam", "your_company");
startActivity(intent);
// API Auth
Intent intent = new Intent(this, VespidWebViewActivity.class);
intent.putExtra("partnerParam", "your_company");
intent.putExtra("token", ssoToken);
startActivity(intent);
import UIKit
import WebKit
class VespidWebViewController: UIViewController, WKScriptMessageHandler {
private static let vespidURL = "https://integration.vespid.my"
private var webView: WKWebView!
var partnerParam: String = ""
var token: String? // For API auth
override func viewDidLoad() {
super.viewDidLoad()
setupWebView()
loadVespid()
}
private func setupWebView() {
let config = WKWebViewConfiguration()
let contentController = WKUserContentController()
// Listen for messages from Vespid
contentController.add(self, name: "vespidHandler")
// Inject message bridge
let script = WKUserScript(
source: """
window.addEventListener('message', function(e) {
if (e.data && e.data.type === 'REQUEST_AUTH_TOKEN') {
window.webkit.messageHandlers.vespidHandler.postMessage('REQUEST_AUTH_TOKEN');
}
});
""",
injectionTime: .atDocumentEnd,
forMainFrameOnly: true
)
contentController.addUserScript(script)
config.userContentController = contentController
webView = WKWebView(frame: view.bounds, configuration: config)
webView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
view.addSubview(webView)
}
private func loadVespid() {
guard let url = URL(string: "\(Self.vespidURL)?partner=\(partnerParam)") else { return }
webView.load(URLRequest(url: url))
}
private func sendToken() {
guard let token = token else { return }
let js = "window.postMessage({type:'AUTH_TOKEN',payload:{token:'\(token)'}},'*');"
webView.evaluateJavaScript(js)
}
// MARK: - WKScriptMessageHandler
func userContentController(_ userContentController: WKUserContentController,
didReceive message: WKScriptMessage) {
if message.body as? String == "REQUEST_AUTH_TOKEN" {
sendToken()
}
}
}
Usage:
// Native Auth
let vc = VespidWebViewController()
vc.partnerParam = "your_company"
navigationController?.pushViewController(vc, animated: true)
// API Auth
let vc = VespidWebViewController()
vc.partnerParam = "your_company"
vc.token = ssoToken
navigationController?.pushViewController(vc, animated: true)
Backend Proxy (Recommended)
Never expose your API key in client code.
- JavaScript (Express)
- Python (Flask)
app.post('/api/vespid/token', async (req, res) => {
const response = await fetch(
'https://api.vespid.my/auth/partners/sso/token',
{
method: 'POST',
headers: {
'X-API-Key': process.env.VESPID_API_KEY,
'Content-Type': 'application/json',
},
body: JSON.stringify({ email: req.body.email }),
}
);
const data = await response.json();
res.json(data.data);
});
import os
import requests
from flask import Flask, request, jsonify
app = Flask(__name__)
@app.route('/api/vespid/token', methods=['POST'])
def get_vespid_token():
response = requests.post(
'https://api.vespid.my/auth/partners/sso/token',
headers={
'X-API-Key': os.environ.get('VESPID_API_KEY'),
'Content-Type': 'application/json',
},
json={'email': request.json.get('email')}
)
data = response.json()
return jsonify(data.get('data'))