Skip to content

MiniApp Platform Spec v1

Scope

MiniApps are partner-provided experiences hosted inside the Emali 2.0 Super App.

v1 supports: - Web miniapps (HTML/JS) executed inside hardened WebView/WKWebView - Capability-based JS bridge - Money movement initiation via Payment Intents (native confirmation required) - Proxy-only authenticated networking for security and auditing

v1 does not support: - Offline transactions - Native miniapps - Arbitrary network egress

Actors

  • User
  • Emali 2.0 Super App (native container)
  • Emali 2.0 BFF/Proxy (server-side enforcement)
  • Partner MiniApp (web bundle)
  • Partner Backend (server-to-server integration)

Design Principles

  • Wallet owns trust: the super-app is the security boundary.
  • Default deny: partners get only explicitly granted capabilities.
  • No token leakage: miniapps never receive refresh tokens.
  • Canonical UX for payments: a native payment sheet always appears.

Distribution and Integrity

Miniapps are distributed as signed bundles.

Required: - manifest.json - bundle signature (algorithm + signature) - allowlisted origins/hosts

Recommended: - Emali 2.0 re-hosts partner bundles on an Emali-controlled CDN.

Manifest (v1)

Minimal fields:

{
  "miniAppId": "eswatini-bank",
  "displayName": "Eswatini Bank",
  "version": "1.0.0",
  "jsApiVersion": 1,
  "runtime": "web",
  "entryUrl": "https://miniapps.test.emali2.damplabs.com/eswatini-bank/index.html",
  "allowedHosts": ["miniapps.test.emali2.damplabs.com"],
  "capabilities": ["payments.initiate", "profile.read"],
  "support": {"website": "https://www.eswatinibank.co.sz"}
}

JSON Schema: miniapps/manifest.schema.json.

MiniApp Runtime

  • Only allow navigations to allowedHosts.
  • Block window.open and new window creation.
  • Disable file and content URL access.

Storage Isolation

  • Each partner gets a unique origin (recommended) to isolate cookies/local storage.

JS Bridge API (v1)

The super-app injects window.emali2.

emali2.runtime.getEnvironment()

Returns API base URLs and runtime metadata.

emali2.payments.requestPayment(request)

Requests a payment intent and shows a native confirmation sheet.

  • The miniapp never executes money movement directly.
  • The super-app decides the step-up auth requirements.

Example request:

{
  "miniAppId": "eswatini-bank",
  "amount": 125.00,
  "currency": "SZL",
  "payeeLabel": "Eswatini Bank Loan Repayment",
  "partnerReference": "LOAN-000123",
  "metadata": {"loanAccount": "EB-000123"}
}

Notes: - currency uses the ISO code (example: SZL). In the UI, amounts are displayed using E.

Example result:

{
  "intentId": "...",
  "status": "completed",
  "receiptNumber": "ABC123XYZ",
  "message": "Payment completed"
}

emali2.qr.scan()

Requests a native QR scan and returns the scanned payload.

Money Movement Policy

  • Miniapps create payment intents.
  • Emali 2.0 shows a native payment sheet.
  • Emali 2.0 executes the payment against core APIs.
  • Emali 2.0 returns results to the miniapp.

Networking Policy

  • Miniapps must not call partner backends directly for authenticated flows.
  • Miniapps call Emali 2.0 BFF/proxy; BFF calls partner backend server-to-server.

Reference Implementation (v1)

Backend service: emali2-miniapps (local dev: http://localhost:8811).

  • GET /v1/miniapps
  • GET /v1/miniapps/{miniAppId}/manifest
  • POST /v1/miniapps/{miniAppId}/session
  • POST /v1/miniapps/{miniAppId}/payment-intents
  • GET /v1/miniapps/{miniAppId}/payment-intents/{intentId}
  • POST /v1/miniapps/{miniAppId}/proxy

Domain Configuration

Examples in this spec use *.test.emali2.damplabs.com, but domains are environment-specific. The Super App must rely on the manifest allowedHosts allowlist so domains can be changed per environment.

Auditing

The BFF records: - miniapp launch/close events - payment intent creation, confirmation, execution - partner API calls (request id, actor, scope)

Versioning

  • JS API is versioned with jsApiVersion.
  • Breaking changes require a new version; older miniapps remain supported.