Install the companion Android app to browse and install release APKs directly from your phone:
Open this page in your phone's browser, tap the button, then open the downloaded APK. You may need to allow "Install unknown apps" for your browser the first time.
Ask your admin to create a token from the admin dashboard, or create one yourself if you have admin access.
Tokens are shown only once — copy it immediately after creation.
Install the dist-ant command:
curl -fsSL https://dist.ant.sh/install.sh | bash
Then configure your credentials:
export DIST_ANT_URL=https://dist.ant.sh
export DIST_ANT_TOKEN=your-token-here
Add the exports to your ~/.bashrc or ~/.zshrc to persist them.
# Upload an APK
dist-ant app-release.apk
# Upload with notes and custom expiration
dist-ant app-release.apk --notes "v1.2.0 beta" --expires 7d
# Upload an iOS IPA
dist-ant MyApp.ipa --notes "TestFlight alternative"
# Get JSON output (for CI parsing)
dist-ant app-release.apk --json
-n, --notes "text" | Release notes |
-e, --expires "30d" | Expiration: 7d, 14d, 30d, 90d, never |
-s, --server URL | Server URL (overrides DIST_ANT_URL) |
-t, --token TOKEN | API token (overrides DIST_ANT_TOKEN) |
-j, --json | Output raw JSON response |
-c, --chunked | Force chunked upload (auto for >95MB) |
Add this task to your app/build.gradle.kts:
tasks.register("uploadApk") {
dependsOn("assembleRelease")
group = "distribution"
description = "Upload release APK to dist-ant"
doLast {
val apkDir = layout.buildDirectory
.dir("outputs/apk/release").get().asFile
val apk = apkDir.listFiles()
?.firstOrNull { it.extension == "apk" }
?: error("No APK found in ${apkDir.absolutePath}")
val serverUrl = project.findProperty("distAntUrl") as? String
?: System.getenv("DIST_ANT_URL")
?: error("Set distAntUrl property or DIST_ANT_URL env var")
val token = project.findProperty("distAntToken") as? String
?: System.getenv("DIST_ANT_TOKEN")
?: error("Set distAntToken property or DIST_ANT_TOKEN env var")
val process = ProcessBuilder(
"curl", "-s", "-X", "POST",
"$serverUrl/api/upload",
"-H", "X-Auth-Token: $token",
"-F", "file=@${apk.absolutePath}",
"-F", "notes=CI build ${project.version}"
).redirectErrorStream(true).start()
val output = process.inputStream.bufferedReader().readText()
val exitCode = process.waitFor()
if (exitCode == 0) {
println("Upload successful: $output")
} else {
error("Upload failed: $output")
}
}
}
# With environment variables
export DIST_ANT_URL=https://dist.ant.sh
export DIST_ANT_TOKEN=your-token-here
./gradlew uploadApk
# Or with Gradle properties
./gradlew uploadApk \
-PdistAntUrl=https://dist.ant.sh \
-PdistAntToken=your-token-here
- name: Build & Upload APK
env:
DIST_ANT_URL: https://dist.ant.sh
DIST_ANT_TOKEN: ${{ secrets.DIST_ANT_TOKEN }}
run: ./gradlew uploadApk
If you prefer not to install anything:
# Upload
curl -X POST https://dist.ant.sh/api/upload \
-H "X-Auth-Token: your-token-here" \
-F "file=@app-release.apk" \
-F "notes=Manual upload" \
-F "expires_in=30d"
# Simple upload (returns just the install URL)
curl -X POST https://dist.ant.sh/upload \
-H "X-Auth-Token: your-token-here" \
-F "file=@app-release.apk"
# Delete a release
curl -X DELETE https://dist.ant.sh/api/delete/RELEASE_ID \
-H "X-Auth-Token: your-token-here"
# List releases
curl https://dist.ant.sh/api/releases
# Latest release for a package
curl https://dist.ant.sh/api/bundle/com.example.app/latest