Electron
For Electron apps built with electron-builder. Create the app with type Electron. One route serves both electron-updater (generic provider) and Electron’s built-in autoUpdater on macOS:
GET https://releaseflow.net/{username}/{slug}/update/{platform}/{version}: update check, JSON or204.GET https://releaseflow.net/{username}/{slug}/update/{platform}/{version}/{fileName}: metadata and installer files of the latest release, redirected tohttps://releaseflow.net/{username}/{slug}/updates/download/{version}/{fileName}.
{platform} is darwin, windows or linux (also mac, win32, appimage and similar aliases). {version} is the running app version and must be SemVer, otherwise the route returns 400.
Publish builds to GitHub
ReleaseFlow reads the GitHub release, so publish there as usual. Keep the files electron-builder generates: the installers, the latest.yml / latest-mac.yml / latest-linux.yml metadata and the .blockmaps.
# electron-builder.yml: publish installers and latest*.yml to GitHub releases
publish:
provider: github
mac:
target: [dmg, zip] # the .zip is what macOS updates installUpdates use the .zip on macOS, the .exe on Windows and the .AppImage on Linux. Add ?arch=arm64, ?arch=x64 or ?arch=universal if you ship separate builds per architecture.
electron-updater (generic provider)
Point the generic provider at the update route with the current version. electron-updater then requests latest-mac.yml (or latest.yml, latest-linux.yml) and the installer relative to that URL, which ReleaseFlow resolves against the latest enabled release.
import { app } from "electron";
import { autoUpdater } from "electron-updater";
const platform = { darwin: "darwin", win32: "windows", linux: "linux" }[
process.platform as "darwin" | "win32" | "linux"
];
autoUpdater.setFeedURL({
provider: "generic",
url: `https://releaseflow.net/{username}/{slug}/update/${platform}/${app.getVersion()}`,
});
autoUpdater.checkForUpdatesAndNotify();Query parameters on the feed URL are kept on every request electron-updater makes, so a beta build only needs ?channel=beta:
autoUpdater.setFeedURL({
provider: "generic",
url: `https://releaseflow.net/{username}/{slug}/update/${platform}/${app.getVersion()}?channel=beta`,
});For a beta or alpha release ReleaseFlow serves beta-mac.yml / alpha-mac.yml when the release has one, and falls back to its latest-mac.yml.
Update JSON route
Without a file name the route answers the update check itself. Electron’s built-in autoUpdater on macOS (Squirrel.Mac) uses exactly this shape:
import { app, autoUpdater } from "electron";
// macOS (Squirrel.Mac): 200 + JSON when a newer .zip exists, 204 otherwise.
autoUpdater.setFeedURL({
url: `https://releaseflow.net/{username}/{slug}/update/darwin/${app.getVersion()}`,
});
autoUpdater.checkForUpdates();When a newer compatible release exists the response is 200:
{
"name": "1.2.0",
"notes": "Release notes from the GitHub release",
"pub_date": "2026-10-21T09:00:00.000Z",
"url": "https://releaseflow.net/{username}/{slug}/updates/download/1.2.0/MyApp-1.2.0-mac.zip"
}When the app is already on the newest version, or nothing matches the platform and architecture, the response is 204 with no body.
Debugging
Every response carries x-releaseflow-debug-reason (update_available, no_newer_release, no_release, missing_asset, unknown_platform, ...) and x-releaseflow-resolved-channel. Add ?debug=1 for a JSON body with the resolved release and asset, and ?sync=1 to sync from GitHub before the lookup:
curl -si "https://releaseflow.net/{username}/{slug}/update/darwin/1.0.0?debug=1"More causes and fixes: /help.
Licensing
Gate the app with the license endpoints over plain fetch; see License keys.