Uploading from Fastlane
Last updated 16 August 2026
If your ad-hoc builds already go out through Fastlane, this is a one-line change. The app_dropper action takes the build your lane just produced, uploads it, and returns a public install link.
Get a token first
Nothing here works without one. See Set up CI uploads.
1. Install the plugin
fastlane add_plugin app_dropperThat adds it to your Pluginfile and updates your Gemfile.lock. Commit both. The plugin has no runtime dependencies — it talks to the API with Ruby’s standard library, so it can’t drag a conflicting HTTP gem into your bundle.
2. Set the token
Export APPDROPPER_TOKEN in your CI environment, or your local .env (gitignored). The action picks it up automatically, so the token never appears in your Fastfile:
export APPDROPPER_TOKEN="adp_…"3. Add it to your lane
With no arguments at all, the action uploads whatever the lane most recently built — it defaults to IPA_OUTPUT_PATH, falling back to GRADLE_APK_OUTPUT_PATH:
platform :ios do
lane :beta do
build_app(scheme: "MyApp", export_method: "ad-hoc")
app_dropper
end
endOr spell it out, which is what most people end up wanting:
platform :ios do
lane :beta do
build_app(scheme: "MyApp", export_method: "ad-hoc")
app_dropper(
api_token: ENV["APPDROPPER_TOKEN"],
file: lane_context[SharedValues::IPA_OUTPUT_PATH],
release_notes: last_git_commit[:message],
tag: "adhoc"
)
end
endAndroid
platform :android do
lane :beta do
gradle(task: "assemble", build_type: "Release")
app_dropper(
file: lane_context[SharedValues::GRADLE_APK_OUTPUT_PATH],
release_notes: last_git_commit[:message]
)
end
endOptions
| Option | Environment variable | Default |
|---|---|---|
api_token | APPDROPPER_TOKEN | required |
file | APPDROPPER_FILE | the lane’s .ipa, else its .apk |
release_notes | APPDROPPER_RELEASE_NOTES | empty |
tag | APPDROPPER_TAG | beta |
timeout | APPDROPPER_TIMEOUT | 600 seconds |
api_token is declared sensitive, so Fastlane redacts it from logs and from the summary table it prints at the end of a run.
Using the result
The action returns the install link, and also puts it — plus the build ID, QR URL and parsed version — into the lane context, so later actions can use them:
lane :beta do
build_app(scheme: "MyApp", export_method: "ad-hoc")
url = app_dropper
slack(
message: "New iOS beta is up",
payload: { "Install" => url, "Version" => lane_context[SharedValues::APP_DROPPER_VERSION] }
)
end| Lane context key | Contains |
|---|---|
APP_DROPPER_INSTALL_URL | Public install link |
APP_DROPPER_BUILD_ID | Identifier of the new build |
APP_DROPPER_QR_URL | PNG QR code for the install link |
APP_DROPPER_VERSION | Version string read out of the binary |
Switching from Diawi
The action is deliberately shaped like the community diawi plugin, so the change is mechanical:
# Before
diawi(
token: ENV["DIAWI_TOKEN"],
file: lane_context[SharedValues::IPA_OUTPUT_PATH],
comment: last_git_commit[:message]
)
# After
app_dropper(
api_token: ENV["APPDROPPER_TOKEN"],
file: lane_context[SharedValues::IPA_OUTPUT_PATH],
release_notes: last_git_commit[:message]
)Both block the lane until the build is processed, and both return a link. What differs:
- App Dropper groups builds into an app with full version history, instead of an unrelated link per upload.
- It notifies your registered testers by email and push.
- Builds are kept 7 days on free, 30 on Pro — or indefinitely if you pin one. See extending and deleting builds.
Troubleshooting
“Couldn’t find a build to upload”
The lane context is empty because no build action ran in this lane, or it ran in a different one. Pass file: explicitly with the path to your artifact.
Token rejected
Revoked, expired, truncated, or belonging to a different app. Run npx appdropper whoami with the same token to see which apps it actually covers.
The upload succeeds but nobody is notified
Notifications go to testers on the app, and a brand-new app has none. Add them in the dashboard — see inviting testers.
Ready to try it?
Drop an .apk or .ipa and get a shareable install link in seconds.