Plugin API

Import from @otakit/capacitor-updater. For normal app code, the main methods are getState(), check(), download(), update(), apply(), and notifyAppReady().

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: "manual",
    // updateMode: "immediate",
  }
}
appId
string
OtaKit app ID for manifest and stats access.
channel
string
Named release track to check. Omit it to use the base stream.
updateMode
'manual' | 'next-launch' | 'immediate'
Overall update behavior. Optional, defaults to next-launch.
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

OtaKit supports three update behaviors:

manual
optional
Do not check automatically. Your app decides when to check, download, and apply.
next-launch
default
Check automatically, download in the background, and activate on the next cold app launch.
immediate
optional
Check automatically, then download and activate immediately during startup.

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.

Normal App Usage

getState() OtaKitState

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

check() LatestVersion | null

Check the configured release target 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

Friendly manual-mode 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

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";

const state = await OtaKit.getState();
const latest = await OtaKit.check();

// Low-level manual flow:
const bundle = await OtaKit.download();
if (bundle) {
  await OtaKit.apply();
}

// Or the one-shot manual helper:
await OtaKit.update();

await OtaKit.notifyAppReady();

Manual update prompt

The normal hosted flow is automatic, but you can also build your own app-level update prompt. A common pattern is: check for an update, show “Update available”, then download and apply it only after the user confirms.

If you want a stricter flow, you can also block parts of your UI until the update is applied. That policy lives in your app code, not in a separate server-side release mode.

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

const state = await OtaKit.getState();

if (state.staged) {
  await OtaKit.apply();
} else {
  const latest = await OtaKit.check();
  if (!latest) {
    return;
  }

  // Show your own dialog / banner here.
  const accepted = window.confirm("Update available. Install now?");

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

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
downloadProgress
{ percent, downloaded, total }
Download progress
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
notifyAppReady() confirmed the running bundle
rollback
{ from, to, reason }
The running bundle rolled back to fallback or builtin
OtaKit.addListener("downloadProgress", (progress) => {
  console.log(`Downloaded: ${progress.percent}%`);
});

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;
}