Skip to main content

Fetch Latest Version of App — Smart Update Links

· 9 min read

Ever needed a download link that always points to the latest version of your app — one you can put on a landing page, in a README, or inside a CI pipeline and never touch again? That's exactly what faynoSync's /apps/latest endpoint is for.

Instead of hardcoding myapp-1.2.3.dmg and updating it on every release, you point users at a stable URL and faynoSync resolves it to the newest matching build. This post walks through the endpoint with copy-paste examples for landing pages, OS auto-detection, shell scripts, and Docker.

Looking to check for updates from inside a running app (Electron, Tauri, Squirrel)? That's a different endpoint — /checkVersion. See How to Setup Auto Update for Electron App. /apps/latest is for download links; /checkVersion is for in-app update checks.


Meet Our Example App 🎮​

Let's imagine we have an app called "SpaceExplorer" with the following setup:

Channels:​

  • 🚀 stable - Production releases
  • 🧪 beta - Testing versions
  • 🌙 nightly - Daily builds

Platforms & Architectures:​

  • 💻 Linux (amd64, arm64)
  • 🍎 macOS (amd64, arm64)
  • 🪟 Windows (amd64)

Package Types:​

  • 📦 .deb / .rpm (Linux)
  • 📦 .dmg (macOS)
  • 📦 .exe (Windows)

Endpoint reference​

GET /apps/latest?app_name=<app_name>&channel=stable&platform=linux&arch=amd64&owner=admin
ParameterRequiredDescription
app_name✅The name of the app to fetch
channel✅*Release channel (stable, beta, nightly)
platform❌Platform (linux, darwin, windows)
arch❌Architecture (amd64, arm64)
package❌Package type (deb, rpm, dmg, exe)
owner✅Your admin user name

* channel becomes mandatory once you've defined channels for the app. If your app has no channels configured, you can omit it.

Two behaviors make this endpoint convenient:

  • Dynamic responses — the more parameters you supply, the narrower the result. Supply only app_name + channel and you get every build in that channel; add platform/arch/package to drill down.
  • Automatic redirection — when your filters resolve to a single build, faynoSync responds with an HTTP redirect straight to the download URL. Perfect for "Download" buttons.

Let's Fetch Some Versions! 🎯​

Example 1: Everything in a channel​

curl --location 'http://localhost:9000/apps/latest?app_name=SpaceExplorer&channel=stable&owner=admin'

Returns all builds in the stable channel, grouped by platform → arch → package:

{
"stable": {
"linux": {
"amd64": {
"deb": { "url": "https://<bucket>.s3.amazonaws.com/SpaceExplorer/stable/linux/amd64/SpaceExplorer-1.2.3.deb", "version": "1.2.3" },
"rpm": { "url": "https://<bucket>.s3.amazonaws.com/SpaceExplorer/stable/linux/amd64/SpaceExplorer-1.2.3.rpm", "version": "1.2.3" }
},
"arm64": {
"deb": { "url": "..." },
"rpm": { "url": "..." }
}
},
"darwin": {
"amd64": { "dmg": { "url": "..." } },
"arm64": { "dmg": { "url": "..." } }
},
"windows": {
"amd64": { "exe": { "url": "..." } }
}
}
}

Example 2: Platform-Specific​

curl --location 'http://localhost:9000/apps/latest?app_name=SpaceExplorer&channel=stable&platform=linux&owner=admin'

Only Linux builds:

{
"stable": {
"linux": {
"amd64": {
"deb": { "url": "..." },
"rpm": { "url": "..." }
},
"arm64": {
"deb": { "url": "..." },
"rpm": { "url": "..." }
}
}
}
}

Example 3: Exact match (auto-redirect to download)​

curl -L --location 'http://localhost:9000/apps/latest?app_name=SpaceExplorer&channel=stable&platform=linux&arch=amd64&package=deb&owner=admin'

Because this resolves to a single build, faynoSync redirects straight to:

https://<bucket>.s3.amazonaws.com/SpaceExplorer/stable/linux/amd64/SpaceExplorer-1.2.3.deb

The -L flag tells curl to follow the redirect and download the file.


Smart Features 🧠​

  1. Automatic Redirection 🔄

    • If only one build matches your filters, you get an HTTP redirect straight to the download.
    • Ideal for landing pages and direct-download links.
  2. Flexible Responses 🎯

    • Get all builds in a channel, or filter by platform, architecture, and package type.
  3. Stable URLs, moving targets 🎯

    • The link never changes; the file it resolves to advances with every release.

Real-World Use Cases 🌍​

1. Landing page download buttons​

<!-- For Linux users -->
<a href="https://updates.yourdomain.com/apps/latest?app_name=SpaceExplorer&channel=stable&platform=linux&arch=amd64&package=deb&owner=admin">
Download for Linux (.deb)
</a>

<!-- For macOS users -->
<a href="https://updates.yourdomain.com/apps/latest?app_name=SpaceExplorer&channel=stable&platform=darwin&arch=arm64&package=dmg&owner=admin">
Download for macOS (.dmg)
</a>

2. One "Download" button that auto-detects the OS​

Detect the visitor's platform in the browser and build the right link on the fly:

<a id="download" href="#">Download SpaceExplorer</a>

<script>
const base = 'https://updates.yourdomain.com/apps/latest';
const ua = navigator.userAgent;
let platform = 'linux', arch = 'amd64', pkg = 'deb';

if (ua.includes('Mac')) { platform = 'darwin'; arch = 'arm64'; pkg = 'dmg'; }
else if (ua.includes('Win')) { platform = 'windows'; arch = 'amd64'; pkg = 'exe'; }

const params = new URLSearchParams({
app_name: 'SpaceExplorer', channel: 'stable', platform, arch, package: pkg, owner: 'admin',
});
document.getElementById('download').href = `${base}?${params}`;
</script>

3. Install the latest build in a shell script​

#!/usr/bin/env bash
set -euo pipefail

URL="https://updates.yourdomain.com/apps/latest?app_name=SpaceExplorer&channel=stable&platform=linux&arch=amd64&package=deb&owner=admin"

curl -L "$URL" -o spaceexplorer.deb
sudo dpkg -i spaceexplorer.deb

4. Always pull the newest build in CI / Docker​

FROM ubuntu:24.04
ARG FAYNOSYNC=https://updates.yourdomain.com/apps/latest?app_name=SpaceExplorer&channel=stable&platform=linux&arch=amd64&package=deb&owner=admin
RUN apt-get update && apt-get install -y curl \
&& curl -L "$FAYNOSYNC" -o /tmp/app.deb \
&& dpkg -i /tmp/app.deb || apt-get -f install -y

5. Beta and nightly channels for testers​

# Beta testers
curl -L 'http://localhost:9000/apps/latest?app_name=SpaceExplorer&channel=beta&platform=darwin&arch=arm64&package=dmg&owner=admin'

# Nightly build enthusiasts
curl -L 'http://localhost:9000/apps/latest?app_name=SpaceExplorer&channel=nightly&platform=linux&arch=amd64&package=deb&owner=admin'

Notes and gotchas​

  • Channel is required once defined. If your app has channels, every /apps/latest request must include channel — otherwise the request is rejected.
  • owner is mandatory for access control; it must be your admin user name.
  • Package types are auto-detected from what's actually uploaded — you only get a dmg/deb/exe key if such a file exists for that platform/arch.
  • "Latest" is per platform and architecture. A release that ships only for Windows does not break your macOS link — each platform/arch resolves to its own newest published build, and each entry reports its version. Builds still in a staged rollout are skipped until they reach 100%. See Latest per Platform and Architecture.
  • Redirect only fires for a single match. If your filters still leave multiple builds (e.g. both .deb and .rpm), you get JSON instead of a redirect — add package to force a direct download.
  • Use HTTPS in production and put faynoSync behind your own domain (e.g. updates.yourdomain.com) so download links stay stable and trusted.

It works for native updater apps too​

/apps/latest isn't limited to plain .deb/.dmg uploads. Apps that use a native updater — Velopack, Squirrel, Sparkle, electron-builder, Tauri — work the same way, and the endpoint returns only their installers. Updater-internal files (release feeds such as RELEASES, releases.{channel}.json, latest.yml or the Sparkle appcast, .nupkg and .delta packages, .blockmap and .sig files) are left out — see Installers Only.

For example, a Velopack app on macOS uploads a full and a delta .nupkg, its release feed, and a .pkg installer. Only the installer is a download target, so the request resolves to a single file:

curl -i 'http://localhost:9000/apps/latest?app_name=HelloVelopack&channel=nightly&platform=darwin&arch=arm64&owner=admin'
HTTP/1.1 302 Found
Location: .../velopack/admin/HelloVelopack/darwin/arm64/installers/HelloVelopack-nightly-Setup-1.0.4-darwin-arm64.pkg

That 302 goes straight to the installer of the newest version — exactly what a "Download for macOS" button needs, no package parameter required. From there, the app keeps itself up to date through its own updater. The full Velopack flow is in Self-Hosted Velopack Update Server: Deltas, Rollbacks, CI.


Everything above assumes a public app. If your app is private, it depends on its download mode:

Download modeWhat /apps/latest needs
Public appNothing
Private, unlistedNothing — the link works for anyone who has it
Private, strictAn X-Download-Token header with the app's fnd_… download token, or a JWT of the owning admin or a team user with the download permission

Two consequences worth knowing:

  • A strict app can't be a plain <a href> link. Browsers don't send custom headers on a link click. Use unlisted for public download pages, or download from a script or backend that adds the header:

    curl -L -H "X-Download-Token: $FAYNOSYNC_DOWNLOAD_TOKEN" \
    'https://updates.yourdomain.com/apps/latest?app_name=SpaceExplorer&channel=stable&platform=linux&arch=amd64&package=deb&owner=admin' \
    -o spaceexplorer.deb
  • A missing token looks like a missing app. Without a valid credential, a strict app answers exactly like an app that doesn't exist, so nobody can probe which private apps you have. Responses of private apps are also never cached in Redis.


/apps/latest vs /checkVersion​

Both deal with "the newest version", but they answer different questions:

/apps/latest/checkVersion
Question"Where do I download the newest build?""Is there an update for my version?"
CallerWebsite, README, script, CIThe running app
Needs the client's versionNoYes
Knows about critical and intermediate versionsNoYes
Typical answerRedirect to the file, or a list of filesupdate_available, changelog, update URLs

Rule of thumb: /apps/latest for first installs, /checkVersion (or a native updater feed) for updates after that. Details: Fetch Latest Version of App and Check Version.


Troubleshooting​

{"error":"Parameters 'app_name' and 'channel' are required"} The app has channels, so channel is mandatory. Add it to the query.

{"error":"app_name not found in apps_meta collection"} Either the app name or owner is wrong, or the app is private in strict mode and the request came without a valid credential.

You get JSON instead of a download. Your filters still match more than one file — for example both .deb and .rpm, or a feed plus an installer. Add package (and platform / arch) until exactly one file matches.

The link points to an old version. /apps/latest only looks at published versions. Check that the newest version is published in the dashboard.


What's Next? 🚀​


How to try faynoSync?​

  1. Follow the Getting Started guide:
    👉 https://faynosync.com/docs/getting-started

  2. Create your app using the REST API or web dashboard:
    📦 API Docs: https://faynosync.com/docs/api
    🖥️ Dashboard UI: https://github.com/ku9nov/faynoSync-dashboard

  3. Upload at least two versions of your application.

  4. Build a smart download link with /apps/latest.


If you find this project helpful, please consider subscribing, leaving a comment, or giving it a star, create an Issue or feature request on GitHub.
Your support keeps the project alive and growing 💚