agentskills.codes
GR

grabjs-superapp-sdk

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.zip

Installs 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.
351 chars✓ has a “when” triggerlonger than Claude Code's old 250-char listing cap (fine on current versions)

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:

CodeTypeDescription
200OKRequest successful, result contains response data
204No ContentRequest successful, no data returned
302RedirectRedirect in progress
400Bad RequestInvalid request parameters
401UnauthorizedAuthentication required
403ForbiddenInsufficient permission (see @requiredOAuthScope tag)
404Not FoundResource not found
424Failed DependencyUnderlying native request failed
426Upgrade RequiredGrab app version too old (see @minimumGrabAppVersion tag)
500Internal ErrorUnexpected SDK error
501Not ImplementedOutside Grab SuperApp environment

Type Guards

Type guards narrow the response type so TypeScript knows which fields are available:

GuardMatches
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:

  1. Call IdentityModule.authorize() to request the scope.
  2. Call ScopeModule.reloadScopes() to refresh the SDK's internal permission state.
  3. 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 demo folder 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.*

Search skills

Search the agent skills registry