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-bridgeStrict and Safe Tracks
Every bridge call comes in two flavours:
- Strict —
send()andrequest()throw a typedBridgeErrorwhen the call can’t be made. - Safe —
send.ifAvailable()andrequest.ifAvailable()never throw; they return aSafeResultyou 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:
| Option | Type | Default | Description |
|---|---|---|---|
timeout | number | 30000 | Timeout in ms |
reqId | string | auto | Request 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.ifAvailableandrequest.ifAvailable. There are nosendIfAvailable/requestIfAvailablenamed 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 — nowindow, 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();| Parameter | Type | Description |
|---|---|---|
authToken | string | undefined | JWT auth token |
contractVersion | Version | undefined | Contract version (semver) |
hostAppVersion | string | undefined | Host app version |
platform | 'ios' | 'android' | undefined | Platform |
startParam | string | undefined | Custom deeplink parameter |
safeAreaInsets | SafeAreaInsets | undefined | Safe area insets |
displayMode | DisplayMode | Display 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
authTokenis the validity marker for launch params — without it,getLaunchParams()returnsundefinedeven if you pass other fields. Always include anauthToken(any non-empty string) in dev mocks.
clearMockLaunchParams
Remove mock launch params from window globals and sessionStorage.
clearMockLaunchParams();Link Interceptor
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
| Option | Type | Default | Description |
|---|---|---|---|
launchParams | Partial<LaunchParams> | see below | Mock launch params |
handlers | Record<MethodName, fn | false> | {} | Custom handlers |
delay | number | 0 | Response delay in ms |
Default launch params: authToken: 'mock-auth-token',
contractVersion: LATEST_VERSION (currently 1.5.0), platform: 'ios',
displayMode: 'standard'.
The default
contractVersionisLATEST_VERSION, so a default mock reports a host that supports every method. To exercise ahost-outdatedpath, setcontractVersionexplicitly (e.g.'1.0.0', which predates notifications).
Set a handler to false to suppress the response entirely.
Instance Methods
| Method | Description |
|---|---|
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.
| Method | Payload | Description | Since |
|---|---|---|---|
app:ready | {} | Signal mini app is ready | 0.0.9 |
app:close | {} | Close the mini app | 1.0.0 |
host.back.button:toggle | { visible } | Show/hide back button | 1.0.0 |
payment:request | See below | Request payment | 0.1.1 |
clipboard:write | { text: string } | Write to clipboard | 0.1.1 |
clipboard:read | {} | Read from clipboard | 0.1.1 |
link:open | { url, openMode? } | Open a URL | 0.1.3 |
haptic:impact | { style } | Impact feedback | 0.2.4 |
haptic:notification | { type } | Notification feedback | 0.2.4 |
haptic:selection | {} | Selection feedback | 0.2.4 |
wallet.solana:connect | {} | Connect Solana wallet | 1.0.0 |
wallet.solana:disconnect | {} | Disconnect wallet | 1.0.0 |
wallet.solana:sign.transaction | { transaction } | Sign tx | 1.0.0 |
wallet.solana:sign.message | { message } | Sign message | 1.0.0 |
wallet.solana:sign.send | { transaction, ... } | Sign+send | 1.0.0 |
notifications:permission.request | {} | Request notification permission | 1.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 handlerinternal: 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.
| Event | Payload | Description | Since |
|---|---|---|---|
host.back.button:clicked | {} | Back button pressed | 1.0.0 |
payment:response | See below | Payment result | 0.1.1 |
clipboard:response | See below | Clipboard read result | 0.1.1 |
wallet.solana:connect.response | See below | Wallet connect result | 1.0.0 |
wallet.solana:sign.transaction.response | See below | Sign tx | 1.0.0 |
wallet.solana:sign.message.response | See below | Sign msg result | 1.0.0 |
wallet.solana:sign.send.response | See below | Sign+send result | 1.0.0 |
notifications:permission.response | { status } | Permission result | 1.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
| Code | Name | Description |
|---|---|---|
5000 | USER_REJECTED | User rejected the request |
-32602 | INVALID_PARAMS | Invalid request parameters |
-32603 | INTERNAL_ERROR | Internal wallet error |
8000 | REQUEST_EXPIRED | Request 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)
}
}| Error | Description |
|---|---|
BridgeError | Base error class |
BridgeTimeoutError | Request timed out (method, timeout) |
BridgeUnavailableError | Bridge not present — outside the app, or SSR/no window |
BridgeMethodUnsupportedError | Host too old for the method (method, contractVersion, minVersion) |
BridgeBusyError | Identical request already in flight — only surfaced by the React hooks (see below) |
LaunchParamsError | Launch 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()throwBridgeUnavailableError— guard them withcallabilityor use the safe variantssend.ifAvailable()/request.ifAvailable()return{ ok: false, error }(never throw)- The React hooks never throw — they report
callable: falseand route any failure to theirerrorstate (they call.ifAvailableinternally) on()/off()work, but with no host attached no events arrive; the returned unsubscribe is realisBridgeAvailable()returnsfalsegetLaunchParams()returnsundefined
Use mockLaunchParamsForDev() to simulate launch params, or
createMockBridge() for full bridge simulation.
Next Steps
- React SDK Overview — higher-level React hooks
- Safe Area Insets — handle notches and home indicators
- Authentication — user authentication
- Payments — accept payments
- Notifications — request permission and send push notifications
- Quickstart — create your first mini app