# SDK API reference

Look up public exports, defaults, hooks, check results and error contracts.

Complete the [minimal integration](/docs/integration) first. Use this page to look up options; you do not need every option. Reuse one client instance for reporting and configuration.

## Overview {#overview}

All public APIs of the Pakta SDK (npm package `rn-update`) are exported from the package entry. Five groups:

- The `Pakta` client class and `ClientOptions`
- The `UpdateProvider` (alias `PaktaProvider`) React integration
- The `useUpdate()` / `useUpdateProgress()` / `usePakta()` hooks
- Update metadata and crash-report correlation
- The `UpdateError` type and the event model

## Client class {#client}

```tsx
import { Pakta } from 'rn-update';

const client = new Pakta({
  appKey: '<your appKey>',
  updateStrategy: 'silentAndLater',
});

// Update configuration at runtime (idempotent merge; re-renders the Provider)
client.setOptions({ checkStrategy: 'onAppResume' });

// Manually report a JS exception (global ErrorUtils is chained automatically)
client.captureException(error, { context: 'checkout' });
```

## ClientOptions {#client-options}

| Field | Type | Default | Description |
| --- | --- | --- | --- |
| `appKey` | `string` | required | Platform appKey assigned by the console |
| `server` | `{ main: string[]; queryUrls?: string[] }` | official endpoints | Self-hosted service address and endpoint discovery list |
| `updateStrategy` | see below | `'alertUpdateAndIgnoreError'` | Prompt and activation strategy |
| `checkStrategy` | `'onAppStart' \| 'onAppResume' \| 'both' \| null` | `'both'` | Check timing; `null` disables JS auto-check |
| `autoMarkSuccess` | `boolean` | `true` | Auto-confirm the running version as healthy |
| `autoMarkSuccessDelayMs` | `number` | `1000` | Auto-confirm delay; raise when critical modules load late |
| `healthCheck` | `() => boolean \| Promise<boolean>` | — | Health gate before confirming; `false` skips this launch |
| `maxRetries` | `number` | `3` | Download retry count |
| `logger` | `({ type, data }) => void` | — | Update event logger |
| `locale` | `'zh' \| 'en'` | `'zh'` | Built-in prompt language |
| `debug` | `boolean` | `false` | Verbose internal logs |
| `throwError` | `boolean` | `false` | Throw update errors to JS |
| `testChannel` | `boolean` | `true` | Honor test QR codes/deep links; use `false` in production |
| `beforeCheckUpdate` / `afterCheckUpdate` | hooks | — | Around every check |
| `beforeDownloadUpdate` / `afterDownloadUpdate` | hooks | — | Around every download |
| `beforeReload` | `(ctx: { type: 'switchVersion' \| 'restartApp' }) => …` | — | Before reload |
| `onPackageExpired` | hook | — | Overrides the expired-package default behavior |
| `disableTelemetry` | `boolean` | `false` | Disable client telemetry and JS error transport |
| `disableErrorReporting` | `boolean` | `false` | Disable JS error reporting only |

`updateStrategy` values: `'silentAndNow'`, `'silentAndLater'`, `'alertUpdateAndIgnoreError'`, `'alwaysAlert'`.

## React integration {#react}

```tsx
import { Pakta, UpdateProvider, PaktaProvider, useUpdate, usePakta, useUpdateProgress } from 'rn-update';
```

- `UpdateProvider` / `PaktaProvider` (alias) — takes a `client` prop; mounting a second provider in the same process throws.
- `useUpdate()` — update state and actions (below).
- `useUpdateProgress()` — isolated download progress `{ hash, received, total, progress? }`.
- `usePakta()` — an alias of `useUpdate()`, returning the same state and actions; access the instance through `useUpdate().client`.

`UpdateContextValue` returned by `useUpdate()`:

| Member | Description |
| --- | --- |
| `updateInfo` | Last `CheckResult` |
| `lastError` | Last error |
| `currentHash` / `packageVersion` | Active update hash and native package version |
| `currentVersionInfo` | `{ name?, description?, metaInfo? }` |
| `checkUpdate(params?)` | Manual check |
| `downloadUpdate(info?)` | Download |
| `switchVersion(info?)` | Switch to the downloaded version now (JS reload) |
| `switchVersionLater(info?)` | Apply on next launch |
| `markSuccess()` | Confirm the current version as healthy |
| `restartApp()` | Restart the app |
| `resetToPackagedBundle(options?)` | Return to the bundled JS |
| `downloadAndInstallApk(url)` | Download and install a native upgrade APK (expired package) |
| `parseTestQrCode(code)` | Parse a test QR code / deep link |
| `dismissError()` | Clear `lastError` |

## CheckResult and version info {#check-result}

```ts
interface CheckResult {
  upToDate?: boolean;      // already latest
  update?: boolean;        // update available
  expired?: boolean;       // native package expired; downloadUrl points to the new binary
  paused?: 'app' | 'package'; // application or native package paused
  downloadUrl?: string;    // expired-binary download address
  bundleStatus?: 'matched' | 'rebuiltSameJs' | 'unknownBundle';
  name?: string;           // version name
  hash?: string;           // version hash
  description?: string;    // version description
  metaInfo?: string;       // custom metadata (JSON string)
  config?: { rollout?: Record<string, number>; forceBoot?: boolean };
  pdiff?: string;          // precise differential package
  diff?: string;           // generic differential package
  full?: string;           // full package
}
```

`bundleStatus` reflects native registration: `matched` — full match; `rebuiltSameJs` — same JS fingerprint but a different build time (full package only); `unknownBundle` — unregistered fingerprint (kept on the current version under strict channel delivery).

## Metadata and crash correlation {#metadata}

```tsx
import * as Sentry from '@sentry/react-native';
import { attachToSentry, getUpdateMetadata } from 'rn-update';

// Call after your existing Sentry initialization.
attachToSentry(Sentry);
const metadata = getUpdateMetadata();
```

`getUpdateMetadata()` returns the current update identity (including `currentVersion`); `updateMetadataTags()` builds key/value pairs ready for Sentry tags.

## Errors and events {#errors}

- `UpdateError` carries a stable machine-readable `code` (`UpdateErrorCode`).
- `EventData` (the `logger` callback) includes `type` (`checking`, `downloading`, `downloadSuccess`, `rollback`, `markSuccess`, `errorUpdate`, …) with error details.
- For JS error reporting and stack symbolication see [Error monitoring](/docs/errors).

Pass an initialized reporter with setAttribute/setAttributes to `attachToCrashlytics(reporter)`. The default tag prefix is `pakta.` and the running update tag is `pakta.currentVersion`.

| Additional option | Default | Purpose |
| --- | --- | --- |
| `disableNativeCheck` | false | Disable native startup checks |
| `dismissErrorAfter` | unset | Clear lastError after this many milliseconds |
| `overridePackageVersion` | native version | Diagnostic JS request override; does not alter the installer |

Production defaults to alertUpdateAndIgnoreError; development defaults to alwaysAlert. debug enables checks/downloads in development, not actual activation. updateStrategy also accepts null, but manual Provider calls can still prompt; use the instance methods below for fully custom UI.

## checkUpdate(params?) {#async-function-checkupdate}

Call inside an UpdateProvider descendant. With no arguments, checks updates for this device, stores updateInfo and runs the configured prompt/download strategy. extra.toHash selects an update for testing; it does not set the native channel. Returns CheckResult or undefined when skipped. Failures go to lastError by default, or throw with throwError enabled. For a raw result and your own UI, use client.checkUpdate() as shown in the manual example.

```typescript
checkUpdate(params?: { extra?: { toHash?: string } }): Promise<CheckResult | undefined>
```

```tsx
const { checkUpdate, lastError } = useUpdate();

async function onCheck() {
  const info = await checkUpdate();
  if (!info) return;
  if (info.expired) {
    return;
  }
  if (info.paused) return;
  if (info.upToDate) return;
  if (info.update) {
  }
}
```


## downloadUpdate(info?) {#async-function-downloadupdate}

Uses the latest check result when info is omitted. Returns true after the Provider download flow; false for no usable update or an afterDownloadUpdate veto, even if files already downloaded. It also runs the activation strategy: silentAndNow requests reload, silentAndLater schedules activation, other values can show a prompt. For download only, client.downloadUpdate(info, onProgress?) returns a hash or undefined; client switching methods accept that hash.

```typescript
downloadUpdate(info?: CheckResult): Promise<boolean | undefined>
```

```tsx
const { updateInfo, downloadUpdate } = useUpdate();

async function onDownload() {
  if (!updateInfo?.update) return;
  const completed = await downloadUpdate(updateInfo);
  if (!completed) return;
}
```


## downloadAndInstallApk(url) {#async-function-downloadandinstallapkurl}

Downloads a complete Android APK and opens the system installer. The URL must use HTTPS and point to an APK, not an AAB, ppk or store page. Non-Android platforms skip this operation. The APK must match the installed application ID and signing identity and satisfy system version requirements. Promise<void> completion does not prove the user installed the app. Default errors appear in lastError; enable throwError for try/catch.

Add REQUEST_INSTALL_PACKAGES inside manifest, outside application, and rebuild the native installer. The SDK uses Android installation sessions; do not add a FileProvider for this API. Plain HTTP URLs are rejected. The button example below belongs under UpdateProvider and receives the real APK URL, optionally from CheckResult.downloadUrl.

On Android 8+, the user must allow this app to install unknown apps. The SDK opens settings when possible and reports APK_INSTALL_PERMISSION_REQUIRED; after granting permission, return and press the button again. Concurrent downloads are skipped. APK_INSTALL_PENDING means the downloaded APK awaits installation; do not loop downloads. For installation failures check the APK, package ID, signature and version. If distribution requires a store upgrade, use your app’s store link instead.

```typescript
downloadAndInstallApk(url: string): Promise<void>
```

```xml title="android/app/src/main/AndroidManifest.xml"
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
```

```tsx title="NativeUpgradeButton.tsx"
import { useState } from 'react';
import { Button, Platform, Text, View } from 'react-native';
import { useUpdate, useUpdateProgress } from 'rn-update';

export function NativeUpgradeButton({ apkUrl }: { apkUrl: string }) {
  const { downloadAndInstallApk, lastError } = useUpdate();
  const progress = useUpdateProgress();
  const [busy, setBusy] = useState(false);
  const [error, setError] = useState('');

  async function install() {
    if (busy || !apkUrl) return;
    setBusy(true);
    setError('');
    try {
      await downloadAndInstallApk(apkUrl);
    } catch (cause) {
      setError(cause instanceof Error ? cause.message : String(cause));
    } finally {
      setBusy(false);
    }
  }

  if (Platform.OS !== 'android') return null;
  const percent = progress?.hash === 'downloadingApk' && progress.total > 0
    ? Math.round(progress.received / progress.total * 100)
    : undefined;
  return (
    <View>
      <Button title={busy ? 'Downloading' : 'Upgrade app'}
        disabled={busy || !apkUrl} onPress={() => { void install(); }} />
      {percent !== undefined ? <Text>{percent}%</Text> : null}
      {error || lastError ? <Text>{error || lastError?.message}</Text> : null}
    </View>
  );
}
```


## markSuccess() {#function-marksuccess}

Confirms that the running update is usable. Returns true when native code accepts, false when rejected, or undefined when already marked, in development or outside the first update launch. Native failures throw. The Provider normally confirms after about 1000 ms. For late initialization set autoMarkSuccess:false, then call after critical initialization succeeds. Do not mark success immediately after download. Errors occurring after confirmation do not necessarily trigger rollback.

```typescript
markSuccess(): Promise<boolean | undefined>
```

```tsx title="Existing client options"
autoMarkSuccess: false,
```

```tsx title="After critical initialization"
const { markSuccess } = useUpdate();

async function confirmReady() {
  try {
    const accepted = await markSuccess();
    if (accepted === false) {
    }
  } catch (error) {
  }
}
```


## currentVersionInfo {#currentversioninfo}

Contains name, description and the JSON-string metaInfo of the running update, or null. updateInfo describes the candidate from the latest check, not the currently running update. currentHash is empty for the embedded bundle; packageVersion is the native installer version and does not change with an update name.

```typescript
currentVersionInfo: {
  name?: string;
  description?: string;
  metaInfo?: string;
} | null
```

```tsx
const { currentVersionInfo, currentHash, packageVersion } = useUpdate();
const label = currentVersionInfo?.name || 'Embedded version';
```


## getCurrentVersionInfo() {#async-function-getcurrentversioninfo}

Compatibility method returning a Promise of name, description and metaInfo. New code should read currentVersionInfo directly.

```typescript
getCurrentVersionInfo(): Promise<{
  name?: string;
  description?: string;
  metaInfo?: string;
}>
```

```tsx
const { getCurrentVersionInfo } = useUpdate();
const version = await getCurrentVersionInfo();
```


## restartApp() {#function-restartapp}

Requests a native restart of the React Native environment. It neither checks nor downloads an update and does not promise an OS-level process kill. Save pending work first. beforeReload receives type restartApp; false cancels. Hook and native restart errors reject and should be caught in button handlers.

```typescript
restartApp(): Promise<void>
```

```tsx
const { restartApp } = useUpdate();
await restartApp();
```


## resetToPackagedBundle(options?) {#async-function-resettopackagedbundleoptions}

Removes downloaded updates and local update state, preserving the device ID. It restores the bundle embedded in the installer, not an arbitrary historical update. By default activation waits for the next launch; restart:true additionally requests restart. A true result proves reset, not successful restart: a hook can veto or restarting can fail. Web returns false; unsupported native builds report RESET_FAILED. Check the result. Stop the faulty server release first or a later check can download it again. See [stop release](/docs/console#stop).

```typescript
resetToPackagedBundle(options?: { restart?: boolean }): Promise<boolean | undefined>
```

```tsx
const { resetToPackagedBundle } = useUpdate();
const reset = await resetToPackagedBundle({ restart: true });
if (!reset) {
}
```


## switchVersion(info?) {#function-switchversion}

Immediately switches to an already downloaded update. Defaults to the latest CheckResult, skips when no hash exists, and resolves true when native reload is requested, not when the next launch proves healthy. Download first. beforeReload can cancel with false. The client-instance equivalent accepts a string: client.switchVersion(hash).

```typescript
switchVersion(info?: CheckResult): Promise<boolean | undefined>
```

```tsx
const { switchVersion, updateInfo } = useUpdate();
if (updateInfo?.hash) await switchVersion(updateInfo);
```


## switchVersionLater(info?) {#function-switchversionlater}

Schedules an already downloaded update for the next launch, preserving the current screen. Defaults to the latest result; missing hash is skipped. Returns Promise<void> and does not invoke beforeReload. Fully close and reopen to test. The client-instance equivalent is client.switchVersionLater(hash).

```typescript
switchVersionLater(info?: CheckResult): Promise<void>
```

```tsx
const { switchVersionLater, updateInfo } = useUpdate();
if (updateInfo?.hash) await switchVersionLater(updateInfo);
```


## parseTestQrCode(code) {#function-parsetestqrcodeqrcode-string}

Accepts a scanned JSON string or UpdateTestPayload object. A true result only means the test payload was recognized, not that download or activation succeeded. Set type to __rnPaktaVersionHash and data to the full update hash. testChannel:false rejects testing payloads. Supply your own scanner UI. For an existing app deep link, append ?type=__rnPaktaVersionHash&data=HASH; the native scheme must already be configured and rebuilt. This differs from the installer’s fixed distribution channel.

```typescript
parseTestQrCode(code: string | UpdateTestPayload): boolean
```

```tsx
const { parseTestQrCode } = useUpdate();
const accepted = parseTestQrCode({
  type: '__rnPaktaVersionHash',
  data: 'REPLACE_WITH_FULL_UPDATE_HASH',
});
```


## useUpdateProgress() {#use-update-progress}

Returns ProgressData or undefined. received and total count bytes; progress is an optional percentage from 0 to 100. APK progress may supply only byte counts. Show an indeterminate state for an unknown total. Use this hook for progress-only components.

```tsx
import { Text } from 'react-native';
import { useUpdateProgress } from 'rn-update';

export function DownloadProgress() {
  const data = useUpdateProgress();
  if (!data) return null;
  const percent = data.progress ?? (data.total > 0
    ? Math.round(data.received / data.total * 100) : undefined);
  return <Text>{percent === undefined ? 'Downloading' : `${percent}%`}</Text>;
}
```


## dismissError() {#dismiss-error}

Clears lastError without retrying or modifying downloaded content. Call when dismissing your error UI. dismissErrorAfter:5000 in client options clears it after five seconds.

```tsx
const { lastError, dismissError } = useUpdate();
```


## Build a custom update UI {#custom-update}

Use instance methods when your UI owns the complete flow. client.downloadUpdate returns a hash, and client switching methods accept a hash; these differ from Hook methods. Add checkStrategy:null and throwError:true to the existing client. Native background checks are separate; disableNativeCheck:true disables them and gives up that recovery path. The example downloads and schedules activation for the next launch, reporting progress and errors.

```tsx title="ManualUpdateButton.tsx"
import { useState } from 'react';
import { Button, Text, View } from 'react-native';
import { useUpdate } from 'rn-update';

export function ManualUpdateButton() {
  const { client } = useUpdate();
  const [busy, setBusy] = useState(false);
  const [message, setMessage] = useState('');
  async function update() {
    if (!client || busy) return;
    setBusy(true);
    try {
      const info = await client.checkUpdate();
      if (!info?.update) {
        setMessage(info?.expired ? '请Upgrade app' : 'No available update');
        return;
      }
      const hash = await client.downloadUpdate(info, (data) => {
        setMessage(data.progress === undefined ? 'Downloading' : `${data.progress}%`);
      });
      if (!hash) return;
      await client.switchVersionLater(hash);
      setMessage('Downloaded; applies on next launch');
    } catch (error) {
      setMessage(error instanceof Error ? error.message : String(error));
    } finally {
      setBusy(false);
    }
  }
  return <View>
    <Button title="Check and download" disabled={busy} onPress={() => { void update(); }} />
    <Text>{message}</Text>
  </View>;
}
```


## Check, download and reload hooks {#lifecycle-hooks}

Add hooks to the existing client. beforeCheckUpdate returning false skips checking. afterCheckUpdate receives completed/skipped/error plus result or error and does not replace the result. beforeDownloadUpdate returning false skips download. afterDownloadUpdate returning false stops Provider post-download actions. onPackageExpired returning false prevents built-in installer-upgrade handling. beforeReload receives type switchVersion/restartApp and optional hash; false or a thrown error prevents reload.

The example calls your own savePendingDrafts function, which returns whether it is safe to continue. Bound asynchronous cleanup so the update UI does not wait indefinitely.

```tsx
beforeReload: async ({ type }) => {
  if (type === 'switchVersion') {
    const saved = await savePendingDrafts();
    return saved;
  }
  return true;
},
```


## Native startup checks {#native-check}

disableNativeCheck defaults to false: native checks run independently after cold starts and can download repairs when JavaScript is unavailable. checkStrategy:null does not turn off that network request. A server forceBoot directive can schedule repair activation for a later launch; local rollback protection still applies. See [brick rescue](/docs/console#rescue).


## captureException(error, context?) {#function-captureexceptionerror-context}

Use the existing client to report caught JavaScript errors. Uncaught errors are reported automatically. disableErrorReporting disables error reporting specifically; disableTelemetry also blocks the related transport. Include only useful diagnostic context. Archive the matching sourcemap and follow [error diagnosis](/docs/errors).

```tsx
const { client } = useUpdate();
try {
  await submitOrder();
} catch (error) {
  client?.captureException(error, { context: 'checkout' });
}
```


## Android mixed apps: setCustomInstanceManager {#updatecontextsetcustominstancemanagerreactinstancemanager-instancemanager}

For an Android host that owns a ReactInstanceManager instead of a standard ReactApplication, register the same live instance after creating it. This only supplies the reload target. The instance still needs UpdateContext.getBundleUrl(context) as its bundle path. Standard RN apps should follow [native integration](/docs/getting-started#android) without creating an additional instance.

```java
import cn.reactnative.modules.update.UpdateContext;

UpdateContext.setCustomInstanceManager(reactInstanceManager);
```
