window.fediInternal
Fedi-specific browser API for mini app discovery, installation, and version detection — not available in standard browsers.
window.fediInternal is a Fedi-only API absent from standard browsers and other Lightning wallets. Unlike WebLN and Nostr, it is optional — older Fedi builds may not inject it, and your app must degrade gracefully.
Version detection
The API is versioned. Check fediInternal.version before calling methods:
type FediInternal =
| { version: 0 }
| { version: 1 }
| { version: 2; getInstalledMiniApps(): Promise<Array<{ url: string }>>; installMiniApp(miniApp: TMiniAppInstall): Promise<void> };
interface TMiniAppInstall {
id: string;
title: string;
url: string;
imageUrl?: string | null;
description?: string;
}| Version | Methods | Notes |
|---|---|---|
0 | None | API present but no callable methods |
1 | None | Placeholder for future expansion |
2 | getInstalledMiniApps, installMiniApp | Current full API |
getInstalledMiniApps()
Lists mini apps installed in the user's Fedi wallet.
// fediInternal.version === 2 only
const apps = await window.fediInternal!.getInstalledMiniApps();
// [{ url: "https://example.com/my-app" }, ...]| Returns | Promise<Array<{ url: string }>> |
| Errors | Network failure, permission denied (manageInstalledMiniApps) |
Use this to deep-link between mini apps or show a "related apps" panel. Call only on user gesture — do not invoke on page load.
Permissions
Both v2 methods require Fedi's manageInstalledMiniApps permission. On first call, Fedi shows an Allow/Deny dialog. If the user denies (especially with "Remember my choice"), subsequent calls reject immediately.
Use isFediPermissionError() from lib/fedi.ts to detect denials:
import { isFediPermissionError } from '../lib/fedi';
try {
await window.fediInternal!.getInstalledMiniApps();
} catch (err) {
if (isFediPermissionError(err)) {
// Show permission guidance + manual retry button
}
}See Fedi permission docs.
installMiniApp(miniApp)
Prompts the user to install a mini app in their Fedi catalog.
| Parameter | Type | Required | Description |
|---|---|---|---|
id | string | yes | Unique app identifier |
title | string | yes | Display name |
url | string | yes | HTTPS URL of the mini app |
imageUrl | string | null | no | Icon URL |
description | string | no | Short description |
| Returns | Promise<void> |
| Errors | User cancellation, invalid URL, permission denied (manageInstalledMiniApps) |
await window.fediInternal!.installMiniApp({
id: 'my-game',
title: 'Sat Stack',
url: 'https://satstack.example.com',
imageUrl: 'https://satstack.example.com/icon.png',
description: 'Stack sats in a tower',
});useFediInternal hook
Base template hook at hooks/useFediInternal.ts:
function useFediInternal(): {
isAvailable: boolean;
version: 0 | 1 | 2 | null;
getInstalledMiniApps: (() => Promise<Array<{ url: string }>>) | null;
installMiniApp: ((miniApp: TMiniAppInstall) => Promise<void>) | null;
}Methods are null when unavailable or version < 2.
const { isAvailable, version, getInstalledMiniApps } = useFediInternal();
if (!isAvailable) return <p>Open in Fedi to see installed apps</p>;
if (version! < 2) return <p>Update Fedi for mini app discovery</p>;useFediBalance hook
Ecash module hook at hooks/useFediBalance.ts:
type TMiniAppsLoadState =
| { status: 'idle' }
| { status: 'loading' }
| { status: 'loaded'; miniApps: Array<{ url: string }> }
| { status: 'permissionDenied' }
| { status: 'error' };
type TFediBalanceState =
| { status: 'loading' }
| { status: 'unavailable' }
| {
status: 'ready';
version: 0 | 1 | 2;
miniAppsLoad: TMiniAppsLoadState;
};
function useFediBalance(): {
state: TFediBalanceState;
loadMiniApps: () => Promise<void>;
};Detects fediInternal version on mount. On v2, call loadMiniApps() from a button click so Fedi can prompt for manageInstalledMiniApps. Surfaces permissionDenied when the user rejects the permission.
Degradation pattern
function MiniAppList() {
const { state, loadMiniApps } = useFediBalance();
if (state.status !== 'ready' || state.version < 2) {
return null;
}
if (state.miniAppsLoad.status === 'idle') {
return <button onClick={() => void loadMiniApps()}>Load installed apps</button>;
}
// loaded | permissionDenied | error ...
}Never assume fediInternal exists. Feature-detect exactly like WebLN and Nostr.
Demo
Visit /demo/ecash in a generated project for FediVersionBadge, BalanceDisplay, and InstallMiniAppButton components.