---
title: "Create an upload"
method: POST
path: "/v1/apps/uploads"
tags: ["uploads"]
---

# Create an upload

`POST /v1/apps/uploads`

## Request

- Base URL: `https://phonebox.dev`
- URL: `https://phonebox.dev/v1/apps/uploads`
- Auth: HTTP bearer

Creates an upload of your own APK and a one-off URL for its file: POST the APK's bytes there with upload_headers within an hour, then complete the upload with the storageId it answers. At most 500 MiB a file and 500 MiB of uploads per project, and 50 new uploads a day; the project needs credits. Completing with a file the project already has ready answers with that upload.

## Request body

- object

## Response `201`

The upload, waiting for its file, with where to send it.

- object
  - `object` 'upload', required
  - `id` string, required
  - `status` 'awaiting_file' | 'processing' | 'ready' | 'invalid_apk' | 'too_large' | 'storage_full' | 'failed' | 'duplicate' | 'deleted', required
  - `package` string, nullable, required: The package the APK's manifest declares, once read.
  - `version_code` integer, nullable, required
  - `version_name` string, nullable, required
  - `min_sdk` integer, nullable, required
  - `size_bytes` integer, nullable, required
  - `sha256` string, nullable, required: The file's SHA-256, in hex.
  - `created_at` string, date-time, required: ISO 8601 timestamp (UTC)
  - `expires_at` string, date-time, required: When Phonebox deletes the upload: 7 days after its last install, or its creation if never installed; an hour after creation while it waits for its file.
  - `error` object, nullable, required: Why the upload ended without becoming ready.
    - `code` 'validation_failed' | 'invalid_request' | 'invalid_api_key' | 'key_expired' | 'key_revoked' | 'unauthorized' | 'insufficient_credits' | 'spend_limit_reached' | 'project_frozen' | 'insufficient_scope' | 'phone_not_allowed' | 'browser_requests_not_allowed' | 'setup_denied' | 'not_found' | 'phone_not_found' | 'file_not_found' | 'app_not_found' | 'upload_not_found' | 'recording_not_found' | 'method_not_allowed' | 'phone_not_running' | 'phone_starting' | 'phone_parking' | 'phone_deleted' | 'phone_failed' | 'idempotency_conflict' | 'running_limit_reached' | 'phone_limit_reached' | 'install_in_progress' | 'version_downgrade' | 'upload_not_ready' | 'storage_limit_reached' | 'clipboard_unavailable' | 'recording_in_progress' | 'recording_not_ready' | 'recording_limit_reached' | 'duplicate_upload' | 'setup_expired' | 'setup_used' | 'key_limit' | 'payload_too_large' | 'target_not_found' | 'ambiguous_target' | 'stale_snapshot' | 'no_focused_field' | 'wait_timeout' | 'app_not_available' | 'invalid_apk' | 'install_failed' | 'feature_unavailable' | 'rate_limited' | 'provider_error' | 'action_outcome_unknown' | 'upload_failed' | 'capacity_unavailable' | 'phone_unavailable' | 'service_paused' | 'storage_full' | 'billing_not_configured' | 'platform_not_configured' | 'provider_timeout' | 'internal_error', required
    - `message` string, required
    - `next` string, nullable, required
  - `upload_url` string, uri, required: Send the APK's bytes here with upload_method and upload_headers, within an hour. The answer is {"storageId": "…"}.
  - `upload_method` 'POST', required
  - `upload_headers` object, required
  - `max_bytes` integer, required

## Other responses

- `400`: Invalid request; details.issues lists each problem as {path, message}
- `401`: Missing or invalid key
- `402`: Insufficient credits, spending limit reached, or the project is frozen
- `403`: Insufficient scope, a phone this key can't use, a browser request, or a setup request that was declined
- `404`: Not found
- `409`: Conflict: the phone's status doesn't allow this, a phone limit is reached, the Idempotency-Key was used with another body, another install is still running on the phone, a newer version of the app is on the phone, an upload that isn't ready, the project's upload storage is full, the clipboard can't be read, a recording is already running or isn't ready, the project keeps 20 recordings, a setup request that expired or already delivered its key, the project has 50 active keys
- `413`: Body too large
- `422`: The request can't be applied, such as an app that isn't in Phonebox's app library, a file that isn't an APK, an install the phone refused, or a feature this phone doesn't offer (feature_unavailable)
- `429`: Rate limited
- `500`: Unexpected server error; keep the request ID
- `502`: The phone service returned an error, or didn't confirm the action
- `503`: No capacity, the phone is briefly unavailable, Phonebox is paused, or payments aren't connected. A batch refused with this error body did nothing on the phone; a 502 or 504 without it, from a proxy or the platform, leaves the outcome unknown.
- `504`: The phone didn't respond in time

---

[API](https://skmtc.dev/phonebox/apis/phonebox-api.md) · [All operations](https://skmtc.dev/phonebox/apis/phonebox-api/llms.txt) · [OpenAPI document](https://skmtc.dev/phonebox/apis/phonebox-api/revisions/87eb2f5211b2?raw)
