Keep installed clients compatible

Prompt:

Stop app versions older than 2.3.0 from calling the backend, and send those users to the store.

Also works: "Which versions of our app are people still using?" "Can I ship this backend change without breaking the old app?" "Force an update of the mobile app."

What your tool does

  • Adds the version header to the one function the app uses for every backend request.
  • Calls read_realm on the environment's realm and reports the versions each declared client stated in the last day.
  • Calls configure_realm with the client's minimum_version and update_url, and reports any warnings the call returns.
  • Reads the x-turnzero-cloud-version header on a backend response to confirm which deployed version handled it.
  • Reads the versions in use again before the backend changes in production, at a deploy on an application with one environment or a promote on one with two. It says which installed versions the new backend may break.

What you need

  • A native app declared on the realm with its client_id, as Sign in from a native app describes.
  • The app's store link, where a refused user is sent to update.

Before your AI starts

This section is for your AI tool: what it checks and gathers before it begins. You don't need to do these steps yourself.

  • A connected, signed-in tool (Connect your tool).
  • The realm's declared clients, read with read_realm on each environment.
  • The app's release version, in the major.minor.patch form the store build uses.

Steps

1. Send the version header from the app

The app states its identity and its version on every request to the backend. The value is the declared client_id, a slash, and the app's version. No test checks this sample.

x-turnzero-cloud-client: com.example.app/2.3.1

Set it in the one function the app uses for every backend request, beside the bearer. The version is major.minor.patch, with an optional pre-release such as 2.4.0-beta.1. A pre-release sorts below its release.

A request without the header is never refused. Neither is one that gives a client the realm does not declare, or a value of another form. The header reaches your backend as the app sent it.

2. Read the versions in use

Every serving router records each declared client's version it sees, at most once a minute. read_realm returns the last day of them beside each client, highest version first. No test checks this sample.

{
  "client_id": "com.example.app",
  "redirect_uris": ["com.example.app:/callback"],
  "minimum_version": "2.0.0",
  "update_url": "https://apps.apple.com/app/id123456789",
  "versions_seen": [
    { "version": "2.3.1", "last_seen": "2026-09-27T09:41:00.000Z" },
    { "version": "2.2.0", "last_seen": "2026-09-27T08:15:00.000Z" }
  ]
}

A version drops out of the list a day after its last request. Each environment's realm keeps its own list, so read the realm of the environment you are changing. A client keeps at most fifty versions at a time.

3. Raise the minimum version

Call configure_realm with the client's minimum_version and its update_url. The change applies at once, with no deploy, and configure_realm lowers or removes it the same way. clients replaces the whole list, so send every client with its current members.

Also raise the minimum when a backend package contract your client's build depends on is retired at the end of its compatibility window. Set it to the first client version built against a contract still in service. Then the version check tells an older client to update, instead of a call failing. The platform enforces the declared value.

Where the new minimum would refuse versions seen in the last day, the response includes warnings. The change still takes effect. No test checks this sample.

{
  "warnings": [
    { "code": "clients_in_use", "client_id": "com.example.app", "versions": ["2.2.0"] }
  ]
}

From then on, the router refuses a request from a lower version with 426 client_upgrade_required. The refusal reaches no backend code and counts toward no request limit.

{
  "error": "client_upgrade_required",
  "detail": "Version 2.2.0 of com.example.app is below 2.3.0, the oldest version this application admits; update the app at https://apps.apple.com/app/id123456789 and try again.",
  "minimum_version": "2.3.0",
  "update_url": "https://apps.apple.com/app/id123456789"
}

Show the user a screen that opens update_url, and stop retrying the request. update_url is null where the client declares none.

4. Read the version that responded

Every response your backend gives through the router includes x-turnzero-cloud-version, the deployed version that handled the request. It matches the version numbers list_versions returns. An environment with no deployed version responds without it.

5. Before a promote

A promote changes the backend every installed app calls, and so does each deploy on an application with one environment, which goes straight to production. Read the versions in use on the production realm first. Where the new backend drops something an installed version still calls, raise the minimum first, or keep the backend compatible until those versions leave the list.

A deploy, a promote, and a rollback never change a minimum, and none of them warns.

Expected result

A request from the app includes x-turnzero-cloud-client, and read_realm lists that version under the client's versions_seen. After the minimum is raised, a request from an older version is refused 426 with the store link. A request from a current version reaches the backend, and its response gives the serving version.

Refusals

Refusal Status Cause Remedy
client_upgrade_required 426 The app stated a version below its client's minimum_version on this realm. Update the app from update_url. Lower the minimum with configure_realm if the refusal was not intended.
invalid_request 400 A minimum_version is not major.minor.patch, or an update_url is not an https URL. Send the three numbers alone, and an https store link.