App Dropper

CLI reference

Last updated 16 August 2026

The appdroppernpm package uploads builds from a terminal, or from any CI that can run a shell command. It’s a thin client over the REST API — everything it does, you could do with curl.

Installing

You don’t have to. npx fetches and runs it in one step, which is what every CI example uses:

bash
npx appdropper upload build.apk

For repeated local use, install it properly:

bash
npm install -g appdropper     # globally
npm install -D appdropper     # or as a dev dependency

Requires Node 18 or newer.

Authentication

A token covers whichever apps you ticked when it was created, so one token — and one appdropper login— can serve every app you push from a machine. Uploading a build whose app isn’t covered fails with a link to add it.

The CLI looks for a token in three places, most explicit first:

  1. The --token flag
  2. The APPDROPPER_TOKEN environment variable — how CI authenticates
  3. A saved login from appdropper login

Prefer the environment variable in CI

A token passed as --tokencan end up in a process list or a log line. One set from your provider’s secret store is masked.

Commands

appdropper upload <file>

Uploads an .apk or .ipa, waits for it to be processed, and prints the install link.

bash
appdropper upload build/app-release.apk
appdropper upload MyApp.ipa --notes "$(git log -1 --pretty=%B)"
appdropper upload build.apk --tag nightly --json
FlagDefaultWhat it does
--token <value>$APPDROPPER_TOKENAPI token to authenticate with
--notes <text>emptyRelease notes shown to testers on the install page
--tag <name>betaLabel for the build. --group is accepted as an alias
--timeout <seconds>600How long to wait for processing before giving up
--jsonoffPrint the full result as JSON instead of a summary
--no-qroffSkip the terminal QR code

On success the install URL — and nothing else — goes to stdout, so URL=$(appdropper upload build.apk)does what you’d expect. Progress bars and status messages go to stderr, so they never contaminate a pipe.

appdropper login

Authorizes this machine. Prints a short code, opens your browser, and waits while you approve it — no password is ever typed into the terminal. On approval the token is written to ~/.appdropper/config with chmod 600.

bash
appdropper login
appdropper login --no-browser   # print the URL instead of opening it

This is for humans. CI has no browser, so pipelines always use a pre-generated token in APPDROPPER_TOKEN instead.

appdropper logout

Removes the saved token from this machine. The token itself stays valid — revoke it in Settings → API tokensif that’s what you meant.

appdropper whoami

Shows which apps the token covers, where it came from, and when it expires. The first thing to run when a pipeline is failing and nobody is sure which credential it’s using — or why an upload was rejected.

bash
$ appdropper whoami
  Token   GitHub Actions adp_a1b2c3d4e5f6g7h8i9j0_••••••wxyz
  Scopes  upload:builds
  Source  APPDROPPER_TOKEN
  Apps    2 apps
    Acme Mobile com.acme.mobile
    Acme Driver com.acme.driver
  Expires 14 Nov 2026

appdropper builds list

Recent builds, newest first. If the token covers more than one app, name the one you want with --appappdropper whoami lists the IDs.

bash
appdropper builds list
appdropper builds list --app p7Rt9... --limit 50 --json

appdropper token rotate

Replaces the current token with a fresh one carrying the same name, apps and validity window. The old value stops working immediately. If you rotated a saved login, the config file is updated in place; otherwise the new token is printed on stdout so you can pipe it somewhere:

bash
# Rotate the CI token and push the new value straight into GitHub Secrets
gh secret set APPDROPPER_TOKEN --body "$(appdropper token rotate --token "$OLD_TOKEN")"

Environment variables

VariablePurpose
APPDROPPER_TOKENThe token to use — the standard way CI authenticates
APPDROPPER_API_URLAPI base URL. Defaults to https://appdropper.io/api/v1
APPDROPPER_CONFIG_DIRWhere the saved login lives. Defaults to ~/.appdropper
NO_COLORDisable coloured output

Exit codes

These are a contract: a pipeline can tell a retryable problem apart from a permanent one.

CodeMeaningRetry?
0Success
1The upload or request failedUsually worth one retry
2Bad arguments — wrong path, wrong file typeNo
3Missing, expired, revoked or out-of-scope tokenNo — fix the credential
4Rate limitedYes, after the stated wait

JSON output

--json prints the raw API result, which is what you want if anything downstream needs more than the link:

json
{
  "upload_id": "8fK2mQ…",
  "status": "ready",
  "build_id": "aBc123XyZ",
  "app_id": "p7Rt9…",
  "app_name": "Acme Mobile",
  "version": "2.4.1",
  "build_number": "318",
  "bundle_id": "com.acme.mobile",
  "platform": "android",
  "install_url": "https://appdropper.io/acme-mobile?build=aBc123XyZ",
  "qr_url": "https://appdropper.io/api/v1/qr/xY9k2.png?build=aBc123XyZ",
  "expires_at": 1786224000000
}

How an upload works

Three requests, which the CLI hides behind one command:

Reserve

The CLI tells the API the file name and size. Every plan limit, storage quota and rate limit is applied here, before a single byte moves, and the API returns a resumable upload URL.

Transfer

The binary goes directly from your machine to Google Cloud Storage — never through our API, which is why build size is capped only by your plan. A dropped connection resumes from where it stopped rather than starting over.

Wait

The CLI holds a long-poll open while the server unpacks the binary, reads its name, icon, version and bundle ID, files it as a build, and notifies your testers. Then it prints the link.

If you’d rather do this yourself, the API reference documents all three.

Ready to try it?

Drop an .apk or .ipa and get a shareable install link in seconds.

Upload a build