Download artifact from private bucket
Artifacts of a private app live in the private bucket and are never served by a public URL. Their links point to the API instead — {API_URL}/download?key=<key> — and this endpoint decides whether the caller may have the file. If so, it answers with a short-lived signed URL.
Access is decided per app, by its download_mode, which is set on create and can be changed on update.
Endpoint
GET /download
Authentication
The endpoint is public; access depends on the app's download_mode and on the credential sent with the request.
| Header | Value | Required |
|---|---|---|
X-Download-Token | fnd_… download token of the app and channel | Conditional |
Authorization | Bearer <jwt_token> | Conditional |
download_mode | Who gets the file |
|---|---|
unlisted | Anyone who knows the key. |
strict | A request with the download token of the artifact's app and channel, or a JWT of the owning admin or of a team user with the download permission and access to the app and channel. |
A JWT with access works in both modes. CI/CD tokens (fns_…) are not accepted on this endpoint.
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
key | string | ✅ | URL-encoded object key from the artifact link |
Example Requests
With a download token (strict app)
curl -X GET --location 'http://localhost:9000/download?key=secondapp-admin%2Fstable%2Flinux%2Famd64%2Fsecondapp-0.0.1.deb' \
--header 'X-Download-Token: fnd_<token>'
With a JWT
curl -X GET 'http://localhost:9000/download?key=secondapp-admin%2Fstable%2Flinux%2Famd64%2Fsecondapp-0.0.1.deb' \
--header 'Authorization: Bearer <jwt_token>'
Response
Download token, or unlisted app (302 Found)
The response redirects to the signed URL of the file.
JWT with access (200 OK)
{
"download_url": "https://<bucket_name>.s3.amazonaws.com/secondapp-admin/stable/linux/amd64/secondapp-0.0.1.deb?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=MdnaDEXKy9nOc4beIvNcgy%2F20250409%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20250409T153628Z&X-Amz-Expires=900&X-Amz-SignedHeaders=host&X-Amz-Signature=bb89a695151c19880c2a2de6620e49a8f532ef1b356542f4821e701c32db9382"
}
Denied or unknown key (404 Not Found)
{
"error": "Artifact not found"
}
A denied request gets exactly the same answer as a key that does not exist, so the endpoint cannot be used to find out which private artifacts exist.
Response Fields
| Field | Type | Description |
|---|---|---|
download_url | string | Signed URL for downloading the file (JWT requests only) |
Notes
- The signed URL expires after 15 minutes
- URL encoding is required for the
keyparameter - Only keys of artifacts that belong to a private app resolve; any other key returns
404 - A download token is scoped to one app and one channel: a token for
betadoes not open an artifact instable - Clients of a
strictapp must send the same header on update checks too, not only on/download - If your HTTP client follows the redirect, do not forward
X-Download-Tokento the storage host ENABLE_PRIVATE_APP_DOWNLOADINGno longer controls this endpoint; it only sets the defaultdownload_modefor new private apps (see environment variables)