API reference for `@grabjs/superapp-sdk`. Use when building a MiniApp that runs in the Grab SuperApp WebView and needs to call native features (camera, payments, authorization, authentication, permission, location, device storage, container UI customization) via the Grab `JSBridge`. Keywords: miniap
Install
mkdir -p .claude/skills/grabjs-superapp-sdk && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/15388" && unzip -o skill.zip -d .claude/skills/grabjs-superapp-sdk && rm skill.zipInstalls to .claude/skills/grabjs-superapp-sdk
Activation
This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.
API reference for `@grabjs/superapp-sdk`. Use when building a MiniApp that runs in the Grab SuperApp WebView and needs to call native features (camera, payments, authorization, authentication, permission, location, device storage, container UI customization) via the Grab `JSBridge`. Keywords: miniapp, webview, android, ios, jsbridge, grab, superapp.About this skill
@grabjs/superapp-sdk
Use this SDK to call native Grab SuperApp features from a MiniApp running in the WebView. Each module covers one domain (camera, payments, location, etc.) and communicates with the native layer via JSBridge.
Setup
Installation
Install @grabjs/superapp-sdk with your package manager of choice.
NPM
npm install @grabjs/superapp-sdk
Yarn
yarn add @grabjs/superapp-sdk
Importing
ES Modules (recommended)
Import only the modules you need:
import { ContainerModule, ScopeModule } from '@grabjs/superapp-sdk';
Type guards and response types are also available as named exports:
import { isSuccess, isError } from '@grabjs/superapp-sdk';
CDN (UMD Bundle)
If you are not using a bundler, load the SDK from a CDN and access it via the SuperAppSDK global.
Always pin to a specific version (e.g., @x.y.z) — omitting the version always fetches the latest release, which may contain breaking changes.
<script src="https://cdn.jsdelivr.net/npm/@grabjs/[email protected]/dist/index.js"></script>
<script>
const { ContainerModule, ScopeModule, isSuccess, isError } = window.SuperAppSDK;
</script>
Core Concepts
SDK methods communicate with the native Grab SuperApp layer via JSBridge. They only work when your page is running inside the Grab SuperApp WebView. Calling a method outside that environment returns { status_code: 501 }.
Response Pattern
Every SDK method returns a response object with an HTTP-style status_code. SDK methods never throw — use type guards instead of try/catch.
import { ProfileModule, isSuccess, isError } from '@grabjs/superapp-sdk';
const profile = new ProfileModule();
const response = await profile.fetchEmail();
if (isSuccess(response)) {
console.log('Result:', response.result);
} else if (isError(response)) {
switch (response.status_code) {
case 403:
// Missing OAuth scope - call IdentityModule.authorize() then ScopeModule.reloadScopes()
break;
case 426:
// Grab app version too old - prompt user to update their app
break;
default:
console.error(`Error ${response.status_code}: ${response.error}`);
}
}
Status Codes
The SDK uses HTTP-style status codes for all responses:
| Code | Type | Description |
|---|---|---|
200 | OK | Request successful, result contains response data |
204 | No Content | Request successful, no data returned |
302 | Redirect | Redirect in progress |
400 | Bad Request | Invalid request parameters |
401 | Unauthorized | Authentication required |
403 | Forbidden | Insufficient permission (see @requiredOAuthScope tag) |
404 | Not Found | Resource not found |
424 | Failed Dependency | Underlying native request failed |
426 | Upgrade Required | Grab app version too old (see @minimumGrabAppVersion tag) |
500 | Internal Error | Unexpected SDK error |
501 | Not Implemented | Outside Grab SuperApp environment |
Type Guards
Type guards narrow the response type so TypeScript knows which fields are available:
| Guard | Matches |
|---|---|
isSuccess(r) | 200, 204 |
isOk(r) | 200 |
isNoContent(r) | 204 |
isRedirection(r) / isFound(r) | 302 |
isClientError(r) | 400, 401, 403, 404, 424, 426 |
isServerError(r) | 500, 501 |
isError(r) | 400, 401, 403, 404, 424, 426, 500, 501 |
import { isSuccess, isOk, isNoContent, isError } from '@grabjs/superapp-sdk';
if (isSuccess(response)) {
// narrow further if needed
if (isOk(response)) console.log(response.result);
if (isNoContent(response)) console.log('done, no data');
}
if (isError(response)) {
// response.error: string is guaranteed here
console.error(response.error);
}
Streams
Some modules provide streaming methods for real-time data (location updates, media events). Subscribe to receive values over time:
import { LocationModule, isSuccess } from '@grabjs/superapp-sdk';
const location = new LocationModule();
const subscription = location.observeLocationChange().subscribe({
next: (response) => {
if (isSuccess(response)) console.log(response.result);
},
complete: () => console.log('Stream ended'),
});
// Always unsubscribe when done to conserve battery and resources
subscription.unsubscribe();
You can also await a stream method directly to get its first value.
Scopes and Permissions
The SDK categorizes permissions into two distinct types based on their execution context:
Permission Types
- Backend Scopes (
openid,profile.read,phone)- Purpose: Access protected resources and user data via your server.
- Flow: Requires a backend token exchange after authorization to retrieve data.
- Mobile Scopes (
mobile.geolocation,mobile.checkout)- Purpose: Access native device capabilities directly within the MiniApp.
- Flow: Grants in-app permission immediately; no backend exchange is necessary.
Authorization Patterns
When designing your MiniApp, you can choose between two common patterns for requesting scopes:
- Upfront Authorization
- Request all required scopes during app initialisation, typically alongside backend sign-in.
- Best for: Core permissions essential for the app to function.
- Deferred Authorization
- Request scopes only when the user triggers a specific feature that requires them.
- Best for: Optional permissions (e.g., location) to improve user experience and build trust.
Permission Verification Strategies
You can verify permissions either proactively before calling a method, or reactively by handling errors.
Proactive Checking
Proactively verify if the current session has the necessary permissions for a method using ScopeModule.hasAccessTo(). This is recommended before calling gated methods, as users can revoke permissions at any time via the Grab app settings.
const scope = new ScopeModule();
const hasAccess = await scope.hasAccessTo('LocationModule', 'getCoordinate');
if (isSuccess(hasAccess) && hasAccess.result) {
// Permission is available, safe to call the method
const location = await location.getCoordinate();
}
Reactive Checking (Handling 403 Forbidden)
Methods tagged with @requiredOAuthScope require specific permissions. If the user hasn't granted the required scope, the method returns 403. You must request authorization and reload scopes before retrying:
- Call
IdentityModule.authorize()to request the scope. - Call
ScopeModule.reloadScopes()to refresh the SDK's internal permission state. - Retry the original method call.
import {
LocationModule,
IdentityModule,
ScopeModule,
isSuccess,
isError,
} from '@grabjs/superapp-sdk';
const location = new LocationModule();
const identity = new IdentityModule();
const scope = new ScopeModule();
const response = await location.getCoordinate();
if (isError(response) && response.status_code === 403) {
// 1. Request authorization for the required scope
const auth = await identity.authorize({
clientId: 'your-client-id',
redirectUri: 'https://your-app.com/callback',
scope: 'mobile.geolocation', // The scope defined in @requiredOAuthScope
environment: 'production',
responseMode: 'in_place',
});
if (isSuccess(auth)) {
// 2. Reload scopes so the new permission is available
await scope.reloadScopes();
// 3. Retry the original call
const retry = await location.getCoordinate();
if (isSuccess(retry)) {
console.log('Result:', retry.result);
}
}
}
Integration Guide
This guide covers the recommended setup for a MiniApp entry point — loading scopes, configuring the container UI, signalling readiness, and handling permissions.
Note: The
demofolder contains two complete MiniApp samples demonstrating these integration patterns in action — one using CDN (vanilla HTML/JS) and one using React. Both implement the same user flow: OAuth authorization, user profile display, deferred location permissions, and checkout payment.
Initialization
Follow these steps when your MiniApp launches to configure the container, authenticate the user, and track the entry event.
import {
ContainerModule,
ScopeModule,
ContainerAnalyticsEventState,
isSuccess,
} from '@grabjs/superapp-sdk';
const container = new ContainerModule();
const scope = new ScopeModule();
async function init() {
// 1. Verify the environment
const connection = await container.isConnected();
if (!isSuccess(connection) || !connection.result?.connected) {
// Handle case where app is opened outside Grab SuperApp
return;
}
// 2. Configure the container UI
await container.setTitle('My MiniApp');
await container.setBa
---
*Content truncated.*