---
title: "Use app updates and version blocking in your app"
description: "Tell users an update is ready, block old versions on the server as well as in the app, and roll releases out by percentage and market."
section: "guides"
status: "Building for 0.7"
spec: "updates"
canonical: "https://mainsdk.dev/docs/guides/use-updates"
updated: "2026-10-28"
---

# Use app updates and version blocking in your app

> Tell users an update is ready, block old versions on the server as well as in the app, and roll releases out by percentage and market.

Status: Building for 0.7 · Canonical: https://mainsdk.dev/docs/guides/use-updates · Updated: 2026-10-28

You will block a version that has a security bug, tell open browser tabs about a new build, and roll a release out to one market first. It takes about 15 minutes.

## You need

- A MainSDK app with a `main.yaml` (see [Thirty-minute path](https://mainsdk.dev/docs/getting-started/thirty-minutes)).
- A client that can send a header: a mobile app, a desktop app or a script. A browser tab needs only step 3.

## 1. Set the minimum with `updates.minVersion`

Declare the oldest client you still serve. The adapters refuse anything older with 426 `MAIN-UPDATE-0001` before a route runs; sign-out and the paths in `exempt` stay open, so a blocked person can still leave and export their data.

```yaml title="main.yaml"
schema: 1
updates:
  min-version: 2.4.0
  exempt: [/api/me/export]
  channels:
    stable: { version: 2.6.0 }
```

```ts title="app/min-version.ts"
import { createTestApp } from "@mainsdk/testing";

const app = createTestApp({ yaml: "schema: 1\nupdates:\n  min-version: 2.4.0\n" });
console.log(app.updates.minVersion());   // "2.4.0"
```

To raise it on a running fleet, push `main.updates.min-version` through [`config`](https://mainsdk.dev/docs/guides/change-config-without-a-deploy). A push can only raise it; a lower value is refused and the refusal is audited.

## 2. Ask with `updates.check`

The client names its version on every request and asks the server whether it must update. Send `versionHeaders(version)` with your requests; the rest is one call.

```ts title="app/client.ts"
import { check, versionHeaders } from "mainsdk/updates/client";

declare function showUpdateScreen(minVersion: string | null): void;   // your own screen

export async function onStart() {
  const status = await check("https://app.example.com", { version: "2.3.9", user: "usr_1" });
  if (status.required) showUpdateScreen(status.minVersion);
  return versionHeaders("2.3.9", "ios");   // add these to every request
}
```

On the server, `app().updates.check({ version, user })` gives the same answer from memory.

## 3. Tell open tabs with `updates.build`

Set the build id in `main.yaml` on each deploy. Open tabs connect to the build stream, learn the id they were not loaded from and show a banner.

```yaml title="main.yaml"
schema: 1
updates:
  build: 2026.10.27-1
```

```ts title="app/build-watch.ts"
import { watchBuild } from "mainsdk/updates/client";

watchBuild({ build: "2026.10.27-1", banner: true });
```

## 4. Roll out with `updates.channel`

Offer a release to a share of users, and all of one market, by a stable hash of the user id. The same user always gets the same answer, and raising the percent never removes anyone.

```ts title="app/rollout.ts"
import { createTestApp } from "@mainsdk/testing";

const app = createTestApp({
  yaml: "schema: 1\nupdates:\n  channels:\n    stable: { version: 2.6.0, rollout: 10, markets: { IN: 100 } }\n",
});
console.log(app.updates.channel({ user: "usr_1", market: "IN" }).version);   // "2.6.0"
```

Change the percent in `main.yaml` through a reviewed change. To stop a bad release, raise `min-version`.

## Check it worked

Run `npx mainsdk check`. It passes when `updates` is configured as shown, and reports `MAIN-UPDATE-0004` if `min-version` is above the stable release. Then send a request with `MainSDK-App-Version: 1.0.0` and expect a 426.
