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 Status is where the work is: Planned (in a milestone, not started), Building (tickets in progress), Preview (usable, may change), then the stability it ships at.
Stability The promise an interface ships with.
- Stable Follows semantic versioning; breaking changes need a new major, 12 months of overlap and a codemod.
- Beta Usable in production; may change before it becomes stable.
- Experimental May change or go away; needs an explicit opt-in in main.yaml.
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). - 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.
schema: 1
updates:
min-version: 2.4.0
exempt: [/api/me/export]
channels:
stable: { version: 2.6.0 }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. 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.
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.
schema: 1
updates:
build: 2026.10.27-1import { 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.
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.
Was this page helpful?