AppsinityAPPSINITY
APIs

Upload API

Uploads are two calls: ask for a signed PUT for one object key, then put the bytes there. The signing service never sees your file, and the file never touches the Studio API.

Open Appsinity Studio

Request a signed URL

One endpoint, two aliases. Both paths do the same thing, and the legacy path exists so older clients keep working.

POST https://<upload-worker>/api/storage/presign-upload
POST https://<upload-worker>/upload-url            (alias)
Authorization: Bearer <studio token>
Content-Type: application/json

{
  "type": "game-build",              // game-build | game-web | app-file | app-media
  "fileName": "my-game-1.4.0.zip",   // sanitised to [\w.-], 140 chars
  "gameId": "my-game",               // required for game-* types
  "appId": "my-game",                // required for app-* types
  "version": "1.4.0",                // defaults to "draft", else Date.now()
  "size": 734003200,                 // bytes, must be > 0
  "contentType": "application/zip",  // optional, capped at 120 chars
  "contentEncoding": "gzip"          // optional
}

200 {
  "uploadUrl":   "<presigned PUT, valid 10800s>",
  "fileUrl":     "https://storage.appsinity.com/public/games/my-game/builds/1.4.0/my-game-1.4.0.zip",
  "key":         "games/my-game/builds/1.4.0/my-game-1.4.0.zip",
  "objectKey":   "same as key",
  "bucket":      "public",
  "contentType": "application/zip",
  "contentEncoding": "",
  "expiresIn":   10800
}
Watch out: The bucket name is part of the public path. A URL that omits /public/ will not resolve for a player, and this is the single most common self-inflicted broken download link.

Object key layout

TypeKey templateNotes
game-buildgames/{gameId}/builds/{version}/{fileName}One directory per version
game-webgames/{gameId}/web/[{version}/]{fileName}The version segment is dropped when version is draft
app-fileapps/{appId}/files/{version}/{fileName}Same versioning as a build
app-mediaapps/{appId}/media/{fileName}No version segment at all
Note: Because an omitted version becomes Date.now(), uploading the same file twice creates two distinct build directories rather than overwriting the first.

The validation gates

Every rule below is enforced by the service, after the sanitisers have already run on your name. The client repeats several of them for a faster message, and the service decides.

RuleDetailError you get
Type must be knownOne of the fourInvalid upload type.
Blocked extensions16 extensions, alwaysBlocked file extension: .exe
Allow-listed extensions13, for all types except game-webFile extension not allowed: .xyz
Size presentA finite number above zeroValid file size is required.
Size ceiling5 GiB, or 250 MiB for app-mediaFile is too large for this upload category.
File name usableAfter sanitising, a name and extension surviveValid fileName is required.
Media is mediaapp-media must be png, jpg, jpeg, webp or mp4App media must be an image or MP4 video.
Web is not an APKgame-web refuses .apk and .aabGame web uploads cannot be APK or AAB files.
IdentifiersgameId or appId present per typegameId is required.
blocked (16): .php .exe .sh .bat .cmd .ps1 .msi .dmg
            .jar .com .scr .vbs .app .xapk .ipa .rar
allowed (13): .apk .aab .zip .html .js .wasm .data
            .json .png .jpg .jpeg .webp .mp4

caps: game-build 5 GiB | game-web 5 GiB | app-file 5 GiB
      app-media 250 MiB
current object key sanitiser: NFKD, keep [\w.-], collapse runs,
                              trim, slice to 140
Watch out: Content type is inferred from the extension when you do not send one: an .aab becomes application/octet-stream and an .html becomes text/html; charset=utf-8. A wrong explicit value is respected, so set it deliberately for a web bundle.

Sending the bytes

  1. PUT to uploadUrl. Use the URL exactly as returned, including its query signature. Any edit invalidates it.
  2. Match Content-Type. Send the returned contentType verbatim; a mismatch is a failed signature check.
  3. Send the body raw. No multipart envelope and no wrapping field.
  4. Do not exceed three hours. A large upload on a slow connection can outlive the URL; the gate is total elapsed time, not transfer time.
  5. Record fileUrl. That is the value your listing's build or web URL must take, including /public/.
  6. Re-request on failure. A new presign is cheap; a retried PUT against an expired URL never succeeds.
Note: Resumable and multipart uploads are not available. One PUT per object, which is why a 5 GiB ceiling and a three-hour window are the practical limit on a single file.

Confirming an object exists

  • No credential is required for this call, because it returns metadata about an object in a public bucket.
  • Only the public bucket is accepted; anything else is Invalid bucket.
  • The check is a HEAD, so it tells you size and type but nothing about contents.
  • Use it after an upload to confirm the object landed with the byte count you sent.
POST https://<upload-worker>/api/storage/file-info
{ "bucket": "public", "key": "games/my-game/builds/1.4.0/my-game-1.4.0.zip" }

200 {
  "bucket": "public",
  "key": "games/...",
  "sizeBytes": 734003200,
  "sizeFormatted": "700.00 MB",
  "contentType": "application/zip",
  "contentEncoding": ""
}

400 { "error": "File info lookup failed (404)." }
Note: A 404 here means the PUT did not complete, not that the object was deleted. Re-presign and re-upload rather than investigating.

Frequently asked questions

Can I upload without a Studio login?

No. Presigning requires a verifiable bearer token, and the two failure messages are Missing login token. and Invalid or expired login token.

Why is my 4 GiB file fine and my 5.5 GiB file refused?

5 GiB is the ceiling for every build type; the client checks first and the service checks again. Split assets or reduce compression rather than retrying.

Is there a delete API?

Not on the upload service. Removal happens through Studio's delete flows and the account-level app deletion OTP route.

Can I list what I have uploaded?

No listing endpoint exists on the storage service. Your build records in Studio are the inventory, see Upload an app.

Need help?

Developer publishing support: [email protected]
General support: [email protected]
Partnerships: [email protected]