Fetch Latest Version of App — Smart Update Links
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/latestis for download links;/checkVersionis 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
| Parameter | Required | Description |
|---|---|---|
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+channeland you get every build in that channel; addplatform/arch/packageto 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 🧠
-
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.
-
Flexible Responses 🎯
- Get all builds in a channel, or filter by platform, architecture, and package type.
-
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/latestrequest must includechannel— otherwise the request is rejected. owneris 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/exekey 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
.deband.rpm), you get JSON instead of a redirect — addpackageto 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.
Download links for private apps
Everything above assumes a public app. If your app is private, it depends on its download mode:
| Download mode | What /apps/latest needs |
|---|---|
| Public app | Nothing |
Private, unlisted | Nothing — the link works for anyone who has it |
Private, strict | An 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
strictapp can't be a plain<a href>link. Browsers don't send custom headers on a link click. Useunlistedfor 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
strictapp 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?" |
| Caller | Website, README, script, CI | The running app |
| Needs the client's version | No | Yes |
| Knows about critical and intermediate versions | No | Yes |
| Typical answer | Redirect to the file, or a list of files | update_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? 🚀
- Serving thousands of download checks per minute? See Performance Mode — Speed Up Your API.
- Want self-updating desktop apps instead of manual downloads? See How to Setup Auto Update for Electron App.
- Running multiple admins with same-named apps? The required
ownerparameter comes from Team-Based Authorization.
How to try faynoSync?
-
Follow the Getting Started guide:
👉 https://faynosync.com/docs/getting-started -
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 -
Upload at least two versions of your application.
-
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 💚
