Plugin API

Import from @otakit/capacitor-updater. For normal app code, the normal hosted flow usually only needs notifyAppReady(). The other public methods exist for advanced manual update flows where your app decides when to check, download, and apply an update.

import { OtaKit } from "@otakit/capacitor-updater";

Configuration

For hosted OtaKit, keep the plugin config small:

plugins: {
  OtaKit: {
    appId: "YOUR_OTAKIT_APP_ID",
    appReadyTimeout: 10000,
    // Optional:
    // updateMode: "next-resume",
  }
}
appId
string
OtaKit app ID for manifest and stats access.
channel
string
Named release track to check. Omit it to use the base channel.
updateMode
'manual' | 'next-launch' | 'next-resume' | 'immediate'
Overall update behavior. Optional, defaults to next-launch.
checkInterval
number
Milliseconds between automatic update checks. Optional, defaults to 600000 (10 min).
appReadyTimeout
number
Milliseconds to wait for notifyAppReady(). Optional, defaults to 10000.

Hosted OtaKit points at the managed server automatically. Do not set serverUrl or manifestKeys unless you intentionally want custom server or verification behavior.

Update Modes

All automatic modes check for updates on cold start (always) and app resume (throttled by checkInterval, default 10 minutes).

next-launch
default
Check and download in the background. Activate the staged bundle only on the next cold start. Zero disruption during a session.
next-resume
recommended
Check and download in the background. Activate the staged bundle on the next resume or cold start. Brief reload when returning to the app.
immediate
dev/debug
Check, download, and activate as soon as possible on both cold start and resume. Primarily for development and testing.
manual
optional
No automatic checks. Your app drives everything via check(), download(), apply(), and update().

Manifest Verification

Hosted OtaKit verifies manifests automatically. The native plugin ships with built-in trusted public keys for the managed service and uses them by default when you stay on the hosted server.

You only need manifestKeys when you intentionally override trust for a custom or self-hosted server. In that case, also set serverUrl to your own API base URL.

Automatic Flow (Default)

This is the normal OtaKit flow. Leave updateMode unset or set it to next-launch. The plugin checks automatically on startup, downloads in the background, and activates the new bundle on the next cold app launch.

In this mode, your app code usually only needs to call notifyAppReady().

notifyAppReady() void

Confirm the current bundle is working. Call this once when your app has fully loaded. If it is not called within appReadyTimeout, the plugin rolls back.

import { OtaKit } from "@otakit/capacitor-updater";

await OtaKit.notifyAppReady();

Manual Flow (Advanced)

Use this only when your app wants to control the update UX itself, for example by showing an “Update available” prompt or delaying install until the user confirms. Set updateMode to "manual" first.

plugins: {
  OtaKit: {
    appId: "YOUR_OTAKIT_APP_ID",
    updateMode: "manual",
    appReadyTimeout: 10000,
  }
}
getState() OtaKitState

Inspect the current app-facing updater state: current bundle, staged bundle, and builtin version.

check() LatestVersion | null

Check the configured channel for a newer bundle without downloading it. When downloaded=true, that exact update is already staged locally.

download() BundleInfo | null

Ensure the latest bundle is staged for later activation. If it is already staged, the staged bundle is returned without re-downloading it.

update() void

Recommended one-shot manual helper. Bring the app to the newest available update now. If the newest update is already staged, apply it. Otherwise download it and apply it. Terminal operation.

apply() void

Activate the currently staged bundle and reload the WebView. Terminal operation.

notifyAppReady() void

Still required after the updated bundle launches. Call this once when your app has fully loaded so the plugin can mark the new bundle healthy.

The simplest manual pattern is: check for updates, show your own prompt, then call update() if the user accepts.

If check() returns downloaded: true, the latest update is already staged locally and you can call apply() directly.

const latest = await OtaKit.check();

if (!latest) {
  return;
}

const accepted = window.confirm("Update available. Install now?");

if (accepted) {
  await OtaKit.update();
}

If you want a split flow, download first and apply later:

const state = await OtaKit.getState();

if (state.staged) {
  await OtaKit.apply();
  return;
}

const latest = await OtaKit.check();
if (!latest) {
  return;
}

const accepted = window.confirm("Update available. Download now?");
if (!accepted) {
  return;
}

await OtaKit.download();

// Later, after another user action:
await OtaKit.apply();

Debug API

Manual inspection and control methods live under OtaKit.debug. These are for diagnostics, support, and test flows, not normal app code.

debug.check(options?) LatestVersion | null

Check the server for a newer bundle without downloading it. You can optionally pass { channel } for a one-off debug override.

debug.download(options?) BundleInfo | null

Debug version of download() that ensures the latest bundle is staged for a one-off { channel } override.

debug.reset() void

Clear active updater state, return to the builtin bundle, clear fallback and last failure state, and reload the WebView. Terminal operation.

debug.listBundles() { bundles: BundleInfo[] }

List downloaded OTA bundles stored on the device.

debug.deleteBundle({ bundleId }) void

Delete a downloaded bundle that is not current, fallback, or staged.

debug.getLastFailure() BundleInfo | null

Return the last failed update metadata for diagnostics. The failed bundle files themselves are cleaned up automatically after rollback.

Advanced Overrides

Use these only when you run a custom server or need custom verification behavior.

plugins: {
  OtaKit: {
    appId: "YOUR_OTAKIT_APP_ID",
    // Optional advanced overrides
    // serverUrl: "https://your-server.com/api/v1",
    // allowInsecureUrls: false,
    // manifestKeys: [
    //   { kid: "key-2026-01", key: "MFkwEwYH..." }
    // ]
  }
}
serverUrl
string
Custom OtaKit server URL. Leave unset for the hosted service default.
allowInsecureUrls
boolean
Allow HTTP for localhost development only. Default: false.
manifestKeys
array
Public verification keys for manifest signature verification on custom/self-hosted setups.

Events

Listen to update lifecycle events with OtaKit.addListener(event, callback). Returns a handle that can be removed with .remove().

downloadStarted
{ version }
A download has begun
downloadComplete
BundleInfo
Download finished and bundle staged
downloadFailed
{ version, error }
Download failed
updateAvailable
LatestVersion
A newer bundle is available. downloaded=true means it is already staged locally.
noUpdateAvailable
App is up to date
appReady
BundleInfo
A newly activated OTA bundle was confirmed healthy by notifyAppReady().
rollback
{ from, to, reason }
The running bundle rolled back to fallback or builtin
OtaKit.addListener("downloadComplete", (bundle) => {
  console.log(`Update staged: ${bundle.version}`);
});

await OtaKit.removeAllListeners();

Types

interface BundleInfo {
  id: string;
  version: string;
  status: "builtin" | "pending"
    | "trial" | "success" | "error";
  downloadedAt?: string;
  sha256?: string;
  channel?: string;
  releaseId?: string;
}

interface OtaKitState {
  current: BundleInfo;
  staged: BundleInfo | null;
  builtinVersion: string;
}

interface LatestVersion {
  version: string;
  url: string;
  sha256: string;
  size: number;
  downloaded?: boolean;
  releaseId?: string;
  minNativeBuild?: number;
}