Uploading from CircleCI
Last updated 16 August 2026
CircleCI needs one runstep and one environment variable. There’s no orb to install.
Get a token first
Nothing here works without one. See Set up CI uploads.
1. Add the token
Two places it can go, depending on how many projects need it:
- One project. Project Settings → Environment Variables → Add Environment Variable. Name it
APPDROPPER_TOKEN. - Several projects. Organization Settings → Contexts. Create a context (say
appdropper), add the variable there, and reference the context from each job. Contexts can be restricted to specific security groups, which is the better choice for a shared credential.
2. Add the upload step
Android
version: 2.1
jobs:
build-android:
docker:
- image: cimg/android:2024.01-node
steps:
- checkout
- run:
name: Build release APK
command: ./gradlew assembleRelease
- run:
name: Upload to App Dropper
command: |
npx appdropper upload \
app/build/outputs/apk/release/app-release.apk \
--notes "$(git log -1 --pretty=%B)"
workflows:
beta:
jobs:
- build-android:
context: appdropper # omit if using a project variableNote the -node image variant — the plain cimg/androidimages don’t all ship Node. On an image without it, install Node first or use the node orb.
iOS
jobs:
build-ios:
macos:
xcode: 15.4.0
steps:
- checkout
# …your existing signing + archive + export steps…
- run:
name: Upload to App Dropper
command: |
npx appdropper upload output/MyApp.ipa \
--notes "$(git log -1 --pretty=%B)" \
--tag adhocmacOS executors have Node preinstalled, so nothing extra is needed there.
Upload only on some branches
The cleanest way is a workflow-level filter, so the job doesn’t even start on branches you don’t distribute:
workflows:
beta:
jobs:
- build-android:
context: appdropper
filters:
branches:
only:
- main
- developTo keep one job and skip only the upload, CircleCI sets $CIRCLE_BRANCH:
- run:
name: Upload to App Dropper
command: |
if [ "$CIRCLE_BRANCH" = "main" ]; then
npx appdropper upload app/build/outputs/apk/release/app-release.apk \
--notes "$(git log -1 --pretty=%B)"
else
echo "Skipping upload on $CIRCLE_BRANCH"
fiUsing the install link
The CLI prints the install URL, and only the install URL, on stdout. CircleCI shares values between steps through $BASH_ENV:
- run:
name: Upload to App Dropper
command: |
INSTALL_URL=$(npx appdropper upload app/build/outputs/apk/release/app-release.apk)
echo "export INSTALL_URL='$INSTALL_URL'" >> "$BASH_ENV"
echo "Install: $INSTALL_URL"
- run:
name: Tell the team
command: |
curl -X POST -H 'Content-type: application/json' \
--data "{\"text\":\"New beta build: $INSTALL_URL\"}" \
"$SLACK_WEBHOOK_URL"JSON output
For anything more involved than a link, --json gives you the whole result — version, build number, platform, bundle ID, install URL, QR URL and expiry:
- run:
name: Upload to App Dropper
command: |
npx appdropper upload app/build/outputs/apk/release/app-release.apk --json > result.json
VERSION=$(jq -r .version result.json)
URL=$(jq -r .install_url result.json)
echo "Uploaded $VERSION → $URL"
- store_artifacts:
path: result.jsonTroubleshooting
“No API token”
If the variable lives in a context, the jobhas to declare that context in the workflow — a context isn’t inherited automatically. If it’s a project variable, note that CircleCI withholds project variables from forked-PR builds unless you’ve explicitly enabled “Pass secrets to builds from forked pull requests”.
“npx: command not found”
The executor image has no Node. Use a -node image variant, or add node/install from the Node orb before the upload step.
The upload runs on every branch
Branch filters go under the job entry in the workflows section, not on the job definition itself — a filter in the wrong place is silently ignored.
Full flag list is in the CLI reference.
Ready to try it?
Drop an .apk or .ipa and get a shareable install link in seconds.