Skip to main content

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​

  1. User opens your app
  2. Your app loads Vespid WebView with partner param
  3. User creates account or logs in on Vespid
  4. User is associated with your partner
  5. 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 AuthAPI Auth
User creates Vespid accountYesNo (automatic)
Requires API keyNoYes
SSO with your appNoYes

Native Auth​

Load Vespid with your partner param. Users authenticate on Vespid.

https://integration.vespid.my?partner=<partner_registration_code>
tip

Examples below use production URLs. For staging, use https://staging-integration.vespid.my and https://api-staging.vespid.my. See Environments.


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

HeaderValueRequired
X-API-KeyPartner API keyYes
Content-Typeapplication/jsonYes

Request Body

FieldTypeDescriptionRequired
emailstringUser's email addressOne of email or external_user_id required
external_user_idstringYour system's unique user identifierOne of email or external_user_id required
display_namestringUser's display nameNo
// 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...' }
}

The WebView requests a token by sending:

{ type: 'REQUEST_AUTH_TOKEN' }
Reply to every REQUEST_AUTH_TOKEN — do not send once and stop

The WebView re-sends REQUEST_AUTH_TOKEN every ~2 seconds until it receives a token. This covers the case where your app hasn't fetched the token yet when the first request arrives.

Your handler must reply to every request you receive, for as long as the WebView is open — not just the first one. Do not use a "send once" flag (e.g. a tokenSent guard). If you only reply once and that reply is missed or arrives before your token is ready, the user will be stuck unauthenticated with no way to recover short of reloading.

If you don't have a token yet when a request arrives, simply ignore it — a later request will succeed once your token is available.


Platform Setup​

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);

// Injects the token via window.postMessage(..., window.location.origin) so the
// resulting MessageEvent has event.source === window and a same-origin event.origin,
// which is required to pass Vespid's message source/origin validation. Sending via
// WebView.postMessage() directly does not satisfy this and will be silently rejected.
const sendToken = useCallback((authToken: string) => {
if (!webViewRef.current) return;
const message = { type: 'AUTH_TOKEN', payload: { token: authToken } };
webViewRef.current.injectJavaScript(`
window.postMessage(${JSON.stringify(message)}, window.location.origin);
true;
`);
}, []);

// Reply to every REQUEST_AUTH_TOKEN — the WebView re-sends this every ~2s
// until it gets a token, so replies must not be gated behind a "sent once" flag.
const handleMessage = useCallback((e: WebViewMessageEvent) => {
if (!token) return;
try {
const data = JSON.parse(e.nativeEvent.data);
if (data.type === 'REQUEST_AUTH_TOKEN') {
sendToken(token);
}
} catch {}
}, [token, sendToken]);

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} />

Never expose your API key in client code.

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);
});