Skip to Content
React SDKBridge Reference
View .md

Alien Mini Apps Bridge API Reference: Core Functions

The @alien-id/miniapps-bridge package is the low-level transport between your mini app and the Alien host app. Use it directly when you need fine-grained control or aren’t using React.

For React apps, prefer the higher-level hooks from @alien-id/miniapps-react — they wrap everything here with state management and capability checks.

Install the Bridge

npm install @alien-id/miniapps-bridge

Strict and Safe Tracks

Every bridge call comes in two flavours:

  • Strictsend() and request() throw a typed BridgeError when the call can’t be made.
  • Safesend.ifAvailable() and request.ifAvailable() never throw; they return a SafeResult you branch on.

Pick Strict when you’ve already checked callability and want exceptions on the unexpected; pick Safe for fire-and-forget UI where a missing capability is a normal, non-exceptional case.

Core Functions: send and request

send

Fire-and-forget method call. No response expected. Throws if the bridge is unavailable.

send('app:ready', {}); send('app:close', {}); send('clipboard:write', { text: 'Hello!' }); send('host.back.button:toggle', { visible: false }); send('link:open', { url: 'https://example.com' }); send('haptic:impact', { style: 'medium' });

request

Send a method and await its response event. Resolves with the response payload, or throws a BridgeError.

const response = await request( 'payment:request', { recipient, amount, token, network, invoice }, 'payment:response', { timeout: 30000 }, );

Options:

OptionTypeDefaultDescription
timeoutnumber30000Timeout in ms
reqIdstringautoRequest ID for correlation

The strict request() gates against the host’s launch-param version. To override which version a call gates against, use the safe variant, which adds a version option.

Safe Variants

The safe variants never throw. Instead they return a SafeResult discriminated union:

type SafeResult<T> = { ok: true; data: T } | { ok: false; error: BridgeError };

The safe variants exist only as attached methods — send.ifAvailable and request.ifAvailable. There are no sendIfAvailable / requestIfAvailable named exports.

send.ifAvailable

Safe version of send(). Returns a SafeResult<void>.

const result = send.ifAvailable( 'haptic:impact', { style: 'medium' }, { version: contractVersion }, ); if (!result.ok) { console.warn('Could not send:', result.error.message); }

request.ifAvailable

Safe version of request(). Returns a Promise<SafeResult<EventPayload>>.

const result = await request.ifAvailable( 'payment:request', { recipient, amount, token, network, invoice }, 'payment:response', { version: contractVersion }, ); if (result.ok) { console.log('Payment:', result.data.status); } else { console.warn('Failed:', result.error.message); }

When you pass a version, both safe variants check method support before executing and surface a BridgeMethodUnsupportedError in result.error if the host is too old.

Capability Checks

Two checks, for two different questions. For a specific method, reach for callability — it already includes the bridge-presence check, so you never pair the two.

isBridgeAvailable

Is the bridge injected at all (i.e. are we inside the Alien app)? This is the method-agnostic check — use it for general “are we in the app” gating, not before a particular call.

if (isBridgeAvailable()) { // Running in the Alien app } else { // Browser dev mode }

callability

Can a specific method be called right now, and if not, why? Returns a Callability discriminated union. A missing bridge surfaces here too, as the no-bridge branch — so callability alone is enough to gate a call.

import { callability } from '@alien-id/miniapps-bridge'; const c = callability('payment:request', { version: contractVersion }); if (c.callable) { // Safe to call } else if (c.reason === 'host-outdated') { console.log(`Needs ${c.needs}, host has ${c.has}`); } else { // c.reason === 'no-bridge' }

In React, the same check is available as the useCallable hook.

Protocol-only check. To test method support against a version without touching the bridge, use isMethodSupported(method, version) from @alien-id/miniapps-contract. It’s a pure version comparison — no window, no host required.

Events

on / off

Subscribe to events from the host app.

const handler = (payload) => { console.log('Back button clicked'); }; on('host.back.button:clicked', handler); // Later: unsubscribe off('host.back.button:clicked', handler);

on() also returns an unsubscribe function:

const unsubscribe = on('host.back.button:clicked', handler); // Later: unsubscribe();

emit

Dispatch an event to local subscribers. emit is typed to the contract’s EventName union — you can only emit real events, not arbitrary strings. Rarely needed by mini apps (the host normally emits events); mostly useful to drive local handlers in tests.

emit('host.back.button:clicked', {});

Launch Parameters

getLaunchParams

Get launch parameters injected by the host app. Returns undefined if unavailable.

const params = getLaunchParams();
ParameterTypeDescription
authTokenstring | undefinedJWT auth token
contractVersionVersion | undefinedContract version (semver)
hostAppVersionstring | undefinedHost app version
platform'ios' | 'android' | undefinedPlatform
startParamstring | undefinedCustom deeplink parameter
safeAreaInsetsSafeAreaInsets | undefinedSafe area insets
displayModeDisplayModeDisplay mode (see below)

DisplayMode values: 'standard' (default), 'fullscreen', 'immersive'. Safe area inset values are in CSS pixels.

retrieveLaunchParams

Strict version of getLaunchParams that throws LaunchParamsError if params are unavailable.

try { const params = retrieveLaunchParams(); } catch (error) { if (error instanceof LaunchParamsError) { console.log('Not running in Alien app'); } }

parseLaunchParams

Parse launch params from a JSON string.

const params = parseLaunchParams(jsonString);

mockLaunchParamsForDev

Simulate launch params during development. Injects values into window globals just like the host app would.

if (process.env.NODE_ENV === 'development') { mockLaunchParamsForDev({ authToken: 'dev-token', platform: 'ios', contractVersion: '1.5.0', displayMode: 'standard', }); }

An authToken is the validity marker for launch params — without it, getLaunchParams() returns undefined even if you pass other fields. Always include an authToken (any non-empty string) in dev mocks.

clearMockLaunchParams

Remove mock launch params from window globals and sessionStorage.

clearMockLaunchParams();

enableLinkInterceptor

Intercept external link clicks and route them through link:open. Same-origin links are unaffected. Returns an unsubscribe function.

const disable = enableLinkInterceptor({ openMode: 'external' }); // Later: disable();

See Link Management for the full interception rules.

Mock Bridge

For testing, use createMockBridge to simulate the host app environment. It injects a mock bridge and launch params into window globals.

import { createMockBridge } from '@alien-id/miniapps-bridge/mock'; const mock = createMockBridge({ launchParams: { authToken: 'test-token', contractVersion: '1.5.0', platform: 'ios', }, handlers: { 'payment:request': (payload) => ({ status: 'paid', txHash: 'mock-tx-123', reqId: payload.reqId, }), }, delay: 100, // simulate async delay in ms }); // Emit events to the miniapp mock.emitEvent('host.back.button:clicked', {}); // Inspect method calls const calls = mock.getCalls(); // Cleanup mock.cleanup();

Options

OptionTypeDefaultDescription
launchParamsPartial<LaunchParams>see belowMock launch params
handlersRecord<MethodName, fn | false>{}Custom handlers
delaynumber0Response delay in ms

Default launch params: authToken: 'mock-auth-token', contractVersion: LATEST_VERSION (currently 1.5.0), platform: 'ios', displayMode: 'standard'.

The default contractVersion is LATEST_VERSION, so a default mock reports a host that supports every method. To exercise a host-outdated path, set contractVersion explicitly (e.g. '1.0.0', which predates notifications).

Set a handler to false to suppress the response entirely.

Instance Methods

MethodDescription
cleanup()Remove mock bridge and clear launch params
emitEvent(name, payload)Emit an event to the miniapp
getCalls()Get array of recorded method calls
resetCalls()Clear recorded calls

Methods Reference

Methods are actions the mini app requests from the host app.

MethodPayloadDescriptionSince
app:ready{}Signal mini app is ready0.0.9
app:close{}Close the mini app1.0.0
host.back.button:toggle{ visible }Show/hide back button1.0.0
payment:requestSee belowRequest payment0.1.1
clipboard:write{ text: string }Write to clipboard0.1.1
clipboard:read{}Read from clipboard0.1.1
link:open{ url, openMode? }Open a URL0.1.3
haptic:impact{ style }Impact feedback0.2.4
haptic:notification{ type }Notification feedback0.2.4
haptic:selection{}Selection feedback0.2.4
wallet.solana:connect{}Connect Solana wallet1.0.0
wallet.solana:disconnect{}Disconnect wallet1.0.0
wallet.solana:sign.transaction{ transaction }Sign tx1.0.0
wallet.solana:sign.message{ message }Sign message1.0.0
wallet.solana:sign.send{ transaction, ... }Sign+send1.0.0
notifications:permission.request{}Request notification permission1.5.0

payment:request

{ recipient: string; // Wallet address amount: string; // Amount in token's smallest unit token: string; // 'USDC', 'ALIEN', or token identifier network: string; // 'solana' or 'alien' invoice: string; // Your order/invoice ID item?: { // Optional display info title: string; iconUrl: string; quantity: number; }; test?: PaymentTestScenario; // Test mode scenario }

Test scenarios: 'paid', 'paid:failed', 'cancelled', 'error:insufficient_balance', 'error:network_error', 'error:unknown'

link:open

{ url: string; openMode?: 'external' | 'internal'; }
  • external (default): Opens in system browser or appropriate app handler
  • internal: Opens within the Alien app (other mini apps, webviews)
send('link:open', { url: 'https://example.com' }); send('link:open', { url: 'solana:...' }); send('link:open', { url: 'mailto:hi@example.com' }); send('link:open', { url: 'https://alien.app/miniapp/other-app', openMode: 'internal', });

haptic:impact

{ style: 'light' | 'medium' | 'heavy' | 'soft' | 'rigid'; }

haptic:notification

{ type: 'success' | 'warning' | 'error'; }

haptic:selection

{}

notifications:permission.request

{}

Prompts the user for OS notification permission. The response carries a NotificationPermissionStatus. See Notifications for the full flow.

wallet.solana:sign.transaction

{ transaction: string; // Base64-encoded serialized transaction }

wallet.solana:sign.message

{ message: string; // Base58-encoded message bytes }

wallet.solana:sign.send

{ transaction: string; // Base64-encoded chain?: SolanaChain; // 'solana:mainnet' etc. options?: { skipPreflight?: boolean; preflightCommitment?: SolanaCommitment; commitment?: SolanaCommitment; minContextSlot?: number; maxRetries?: number; }; }

SolanaChain values: 'solana:mainnet', 'solana:devnet', 'solana:testnet'.

SolanaCommitment values: 'processed', 'confirmed', 'finalized'.

Bridge Events and Callbacks

Events are notifications from the host app to the mini app.

EventPayloadDescriptionSince
host.back.button:clicked{}Back button pressed1.0.0
payment:responseSee belowPayment result0.1.1
clipboard:responseSee belowClipboard read result0.1.1
wallet.solana:connect.responseSee belowWallet connect result1.0.0
wallet.solana:sign.transaction.responseSee belowSign tx1.0.0
wallet.solana:sign.message.responseSee belowSign msg result1.0.0
wallet.solana:sign.send.responseSee belowSign+send result1.0.0
notifications:permission.response{ status }Permission result1.5.0

payment:response

{ reqId: string; status: 'paid' | 'cancelled' | 'failed'; txHash?: string; errorCode?: PaymentErrorCode; }

Error codes: insufficient_balance, network_error, unknown

clipboard:response

{ reqId: string; text: string | null; errorCode?: 'permission_denied' | 'unavailable'; }

notifications:permission.response

{ reqId: string; status: 'granted' | 'denied' | 'rate_limited'; }

wallet.solana:connect.response

{ reqId: string; publicKey?: string; // Base58-encoded errorCode?: WalletSolanaErrorCode; errorMessage?: string; // Human-readable error }

wallet.solana:sign.transaction.response

{ reqId: string; signedTransaction?: string; // Base64-encoded errorCode?: WalletSolanaErrorCode; errorMessage?: string; }

wallet.solana:sign.message.response

{ reqId: string; signature?: string; // Base58-encoded publicKey?: string; // Base58-encoded errorCode?: WalletSolanaErrorCode; errorMessage?: string; }

wallet.solana:sign.send.response

{ reqId: string; signature?: string; // Base58-encoded errorCode?: WalletSolanaErrorCode; errorMessage?: string; }

Wallet Error Codes

CodeNameDescription
5000USER_REJECTEDUser rejected the request
-32602INVALID_PARAMSInvalid request parameters
-32603INTERNAL_ERRORInternal wallet error
8000REQUEST_EXPIREDRequest expired / timed out

Error Handling

send() and request() throw typed errors, all subclasses of BridgeError:

import { BridgeError, BridgeTimeoutError, BridgeUnavailableError, BridgeMethodUnsupportedError, } from '@alien-id/miniapps-bridge'; try { const result = await request( 'payment:request', params, 'payment:response', ); } catch (error) { if (error instanceof BridgeTimeoutError) { // error.method, error.timeout } else if (error instanceof BridgeMethodUnsupportedError) { // error.method, error.contractVersion, error.minVersion } else if (error instanceof BridgeUnavailableError) { // Not running in Alien app (also covers the SSR / no-window case) } }
ErrorDescription
BridgeErrorBase error class
BridgeTimeoutErrorRequest timed out (method, timeout)
BridgeUnavailableErrorBridge not present — outside the app, or SSR/no window
BridgeMethodUnsupportedErrorHost too old for the method (method, contractVersion, minVersion)
BridgeBusyErrorIdentical request already in flight — only surfaced by the React hooks (see below)
LaunchParamsErrorLaunch params unavailable (retrieveLaunchParams)

BridgeUnavailableError and BridgeMethodUnsupportedError come straight from the callability gate. BridgeBusyError is different: raw send/request never throw it — it’s synthesized by the React hooks (useMethod, useClipboard, useNotificationPermission) when you call them while an identical request is still in flight, and it arrives via the hook’s error state, not a throw.

The React package throws no errors of its own — hooks surface these same bridge error types directly, so a single instanceof check works everywhere.

Trust Model

The bridge message listener does not restrict messages by event.origin. Treat any host-delivered payload as input to validate, and never embed third-party content that could post messages into your mini app’s window.

Dev Mode Behavior

When running in a browser (bridge unavailable):

  • send() / request() throw BridgeUnavailableError — guard them with callability or use the safe variants
  • send.ifAvailable() / request.ifAvailable() return { ok: false, error } (never throw)
  • The React hooks never throw — they report callable: false and route any failure to their error state (they call .ifAvailable internally)
  • on() / off() work, but with no host attached no events arrive; the returned unsubscribe is real
  • isBridgeAvailable() returns false
  • getLaunchParams() returns undefined

Use mockLaunchParamsForDev() to simulate launch params, or createMockBridge() for full bridge simulation.

Next Steps

Last updated on