Skip to main content

Upload App

Upload a new version of an application, including the binary file and relevant metadata. You can specify an updater type for artifact isolation when uploading files for updaters that generate special files.

Endpoint​

POST /upload

Authentication​

HeaderValue
AuthorizationBearer <jwt_token>

Request Body​

The request uses multipart/form-data format with the following fields:

FieldTypeRequiredDescription
filefile✅App binary file(s)
app_namestring✅Name of the application
versionstring✅Version of the application
channelstring❌Channel (e.g., stable, beta)
publishboolean❌Marks the version available for users
criticalboolean❌Marks the version as critical
platformstring❌Platform (e.g., linux, windows)
archstring❌Architecture (e.g., amd64, arm64)
changelogstring❌Changelog in markdown format
updaterstring❌Updater type for artifact isolation (manual, velopack, sparkle, squirrel_darwin, squirrel_windows, electron-builder, tauri)
signaturestring❌Cryptographic signature (required for Tauri updater)
Large files

Every file sent to this endpoint passes through faynoSync and the reverse proxy in front of it. For large builds, use presigned uploads (Upload Init + Upload Complete) to send files directly to object storage.

info

The updater parameter is useful when uploading files for updaters that generate or ingest special files (like RELEASES for Squirrel Windows, *.yml for electron-builder, releases.{channel}.json for Velopack, or appcast.{channel}.xml for Sparkle). It ensures files are stored in isolated folders specific to that updater.

Example Requests​

Single File Upload (Standard)​

curl -X POST --location 'http://localhost:9000/upload' \
--header 'Authorization: Bearer <jwt_token>' \
--form 'file=@"/path_to_file/myapp.deb"' \
--form 'data="{\"app_name\":\"myapp\",\"version\":\"0.0.1\",\"channel\":\"stable\",\"publish\":true,\"platform\":\"linux\",\"arch\":\"amd64\",\"changelog\":\"### Changelog\\n\\n- Added new feature X\\n- Fixed bug Y\"}"'

Upload with Updater Parameter (Squirrel Windows)​

curl -X POST --location 'http://localhost:9000/upload' \
--header 'Authorization: Bearer <jwt_token>' \
--form 'file=@"/path_to_file/myapp.exe"' \
--form 'file=@"/path_to_file/RELEASES"' \
--form 'data="{\"app_name\":\"myapp\",\"version\":\"0.0.1\",\"channel\":\"stable\",\"publish\":true,\"platform\":\"windows\",\"arch\":\"amd64\",\"updater\":\"squirrel_windows\",\"changelog\":\"### Changelog\\n\\n- Added new feature X\\n- Fixed bug Y\"}"'

Upload with Updater Parameter (Electron Builder)​

curl -X POST --location 'http://localhost:9000/upload' \
--header 'Authorization: Bearer <jwt_token>' \
--form 'file=@"/path_to_file/myapp.exe"' \
--form 'file=@"/path_to_file/latest.yml"' \
--form 'data="{\"app_name\":\"myapp\",\"version\":\"0.0.1\",\"channel\":\"stable\",\"publish\":true,\"platform\":\"windows\",\"arch\":\"amd64\",\"updater\":\"electron-builder\",\"changelog\":\"### Changelog\\n\\n- Added new feature X\\n- Fixed bug Y\"}"'

Upload with Updater Parameter (Tauri)​

curl -X POST --location 'http://localhost:9000/upload' \
--header 'Authorization: Bearer <jwt_token>' \
--form 'file=@"/path_to_file/myapp.app.tar.gz"' \
--form 'data="{\"app_name\":\"myapp\",\"version\":\"1.0.0\",\"channel\":\"stable\",\"publish\":true,\"platform\":\"darwin\",\"arch\":\"amd64\",\"updater\":\"tauri\",\"signature\":\"dW50cnVzdGVkIGNvbW1lbnQ6IHNpZ25hdHVyZSBmcm9tIHRhdXJpIHNlY3JldCBrZXkKUlVUUzIzUGJLcHpQNHdCVEJzTXJjbWFhUUdUVEtaUGRLdGhWdGZJZkF2VmM0TzJGMkdPQUk4V1hzYWJuUUhWbWI2cTFWTkhEZE9lbkVwUERCQmhRPT0K\",\"changelog\":\"### Changelog\\n\\n- Added new feature X\\n- Fixed bug Y\"}"'

Upload with Updater Parameter (Sparkle, macOS)​

Sparkle has no signature field — the edSignature already lives inside the uploaded appcast. Upload the generate_appcast output plus every archive/delta it references:

curl -X POST --location 'http://localhost:9000/upload' \
--header 'Authorization: Bearer <jwt_token>' \
--form 'file=@"/path_to_file/appcast.stable.xml"' \
--form 'file=@"/path_to_file/MyApp-1.0.0.zip"' \
--form 'data="{\"app_name\":\"myapp\",\"version\":\"1.0.0\",\"channel\":\"stable\",\"publish\":true,\"platform\":\"darwin\",\"arch\":\"arm64\",\"updater\":\"sparkle\",\"changelog\":\"### Changelog\\n\\n- Added new feature X\\n- Fixed bug Y\"}"'

Multiple File Upload (Standard)​

curl -X POST --location 'http://localhost:9000/upload' \
--header 'Authorization: Bearer <jwt_token>' \
--form 'file=@"/path_to_file/myapp.deb"' \
--form 'file=@"/path_to_file/myapp.rpm"' \
--form 'data="{\"app_name\":\"myapp\",\"version\":\"0.0.1\",\"channel\":\"stable\",\"publish\":true,\"platform\":\"linux\",\"arch\":\"amd64\",\"changelog\":\"### Changelog\\n\\n- Added new feature X\\n- Fixed bug Y\"}"'

Response​

Success Response (200 OK)​

{
"uploadResult.Uploaded": "6411c7c0ec4ff9a9a9bc18fa"
}

Response Fields​

FieldTypeDescription
uploadResult.UploadedstringThe unique identifier (ID) of the uploaded version

Updater-Specific File Organization​

When you specify an updater parameter, files are organized in isolated folders:

File Structure Examples​

Standard Upload (No Updater):

/myapp/stable/linux/amd64/
├── myapp-0.0.1.deb
└── myapp-0.0.1.rpm

Squirrel Windows Upload:

/squirrel_windows/myapp/stable/windows/amd64/0.0.1/
├── myapp.exe
└── RELEASES

Electron Builder Upload:

/electron-builder/myapp/stable/windows/amd64/0.0.1/
├── myapp.exe
└── latest.yml

Sparkle Upload (materialized per platform/arch):

/sparkle/admin/myapp/darwin/arm64/
├── MyApp-1.0.0.zip
└── appcast.stable.xml

Supported Updater Types​

UpdaterGenerated FilesUse CaseSpecial Requirements
manualNoneStandard FaynoSync uploadsNone
velopackreleases.{channel}.jsonVelopack cross-platform desktop appsRequires channel, platform, and arch
sparkleappcast.{channel}.xmlNative macOS apps using SparklemacOS only; upload the generate_appcast appcast + archives
squirrel_darwinNoneSquirrel macOS applicationsNone
squirrel_windowsRELEASESSquirrel Windows applicationsNone
electron-builder*.ymlElectron applicationsNone
tauriNoneTauri applications with built-in updaterRequires signature field

Notes​

  • You can upload multiple files for different platforms/architectures in a single request
  • The changelog field supports markdown formatting
  • Critical versions are typically used for security updates or critical bug fixes
  • Published versions are immediately available to end users
  • The channel field helps organize releases (e.g., stable, beta, nightly)
  • File uploads support common package formats (.deb, .rpm, .exe, .dmg, etc.)
  • Updater Parameter: Use the updater parameter when uploading files for updaters that generate special files (RELEASES, *.yml)
  • Artifact Isolation: Files uploaded with an updater parameter are stored in isolated folders to prevent conflicts
  • File Organization: Generated files (RELEASES, *.yml) are properly organized when using updater-specific uploads
  • Signature Field: When using Tauri updater, the signature field is required for cryptographic verification of updates. This signature is generated by Tauri's build process and ensures update integrity and authenticity.
  • Private Apps: a private app accepts only the manual, tauri and squirrel_darwin updaters. velopack, sparkle, electron-builder and squirrel_windows need a publicly served update feed, so they are rejected with 400: updater is not supported for private apps: <updater> requires a publicly served update feed; use manual, tauri or squirrel_darwin, or a public app
  • Uploading to an app that does not exist returns 404
  • Team users: the app, channel, platform and architecture must all be in the team user's allowed lists (see Team Based Authorization). Otherwise the request returns 403 with you don't have access to this <app|channel|platform|architecture>, and no file is written to storage. Before v2.4.0 these lists were not checked on this endpoint.