# Files

## List files[​](#list-files "Direct link to List files")

GET/api/v1/files

The organization's unexpired files, newest first. Any role.

Query parameters

`sha256`string

Only the file with this lowercase hex SHA-256.

`app`string · uuid

Only files of this app.

`platform`string

Only files for this platform.

`bundleId`string

Only files with this bundle id.

`branch`string

Only files whose build.branch equals this.

`commit`string

Only files whose build.commit equals this.

`status`string

Only files in this status. Without it, ready and processing files are listed; pendingUpload and failed are for debugging.

`q`string

Case-insensitive substring of the name, filename or bundle id.

`limit`integer

Page size, 1 to 100. Default 25.

`cursor`string

\`nextCursor\` from the previous page. Opaque.

Header parameters

`X-Organization-Id`string

Selects which of the caller's organizations the request acts on. When omitted, the caller's default organization is used.

Returns

`object`stringrequired

`data`arrayrequired

Show child attributes

`id`string · uuidrequired

`status`stringrequired

pendingUpload: bytes not verified yet. processing: verification running (seen only by a concurrent caller). ready: installable. failed: see failureReason; creating the same sha256 again resets it.

`app`string · uuidrequirednullable

The app this file belongs to, once metadata is extracted.

`platform`stringrequirednullable

Known once metadata is extracted.

`filename`stringrequired

`filesize`integer · int64required

`sha256`stringrequired

`name`stringrequired

Display name. Defaults to the filename until metadata is extracted.

`bundleId`stringrequirednullable

Android package name or iOS bundle identifier.

`version`stringrequirednullable

Version name (Android) or CFBundleShortVersionString (iOS).

`buildNumber`stringrequirednullable

Version code (Android) or CFBundleVersion (iOS).

`minOsVersion`stringrequirednullable

Minimum OS version the build supports.

`isSimulator`booleanrequired

True for an iOS simulator build.

`build`objectrequirednullable

Show child attributes

`branch`string

`commit`string

`ciRunUrl`string · uri

https only. Stored and displayed, never fetched.

`source`stringrequired

How the file was created, derived from the credential.

`organization`stringrequired

Organization that owns the file.

`user`stringrequirednullable

Uploader; null if the user was deleted.

`failureReason`stringrequirednullable

Why the file failed; null otherwise.

`createdAt`string · date-timerequired

`lastUsedAt`string · date-timerequirednullable

Last dedup hit or install.

`expiresAt`string · date-timerequired

The file and its bytes are deleted after this.

`nextCursor`string

Pass as \`cursor\` for the next page. Absent on the last page.

Request

```
curl https://app.mobilenext.ai/api/v1/files \

  -X GET \

  -H 'Authorization: Bearer mob_...'
```

Response

200400401

A page of files

```
{

  "object": "list",

  "data": [

    {

      "id": "string",

      "status": "pendingUpload",

      "app": "string",

      "platform": "android",

      "filename": "app-release.apk",

      "filesize": 48211033,

      "sha256": "string",

      "name": "Checkout fix",

      "bundleId": "com.acme.shop",

      "version": "4.2.0",

      "buildNumber": "4201",

      "minOsVersion": "8.0",

      "isSimulator": false,

      "build": {

        "branch": "fix/checkout",

        "commit": "3dbd301",

        "ciRunUrl": "https://github.com/acme/shop/actions/runs/991"

      },

      "source": "api",

      "organization": "string",

      "user": "string",

      "failureReason": "checksum_mismatch",

      "createdAt": "2026-01-01T00:00:00Z",

      "lastUsedAt": "2026-01-01T00:00:00Z",

      "expiresAt": "2026-01-01T00:00:00Z"

    }

  ],

  "nextCursor": "string"

}
```

## Create a file[​](#create-file "Direct link to Create a file")

POST/api/v1/files

Declares a file by its SHA-256. If the organization already stores that hash, the stored file is returned instead of a new one.

**PUT the bytes if and only if the response contains `upload`.** Do not branch on the status code. Then call `completeFile` unless the file is already `ready`.

| Stored file with this sha256 | Response                                 | `upload`       |
| ---------------------------- | ---------------------------------------- | -------------- |
| none                         | `201`, `pendingUpload`                   | yes            |
| `pendingUpload` or `failed`  | `200`, same id, reset to `pendingUpload` | yes, fresh URL |
| `processing`                 | `200`                                    | no             |
| `ready`                      | `200`, expiry extended, `lastUsedAt` set | no             |

A dedup hit keeps the first create's `filename`, `name`, `build` and `user`, and never shortens the expiry. Rate limited to 100 creates per 15 minutes per organization.

Header parameters

`X-Organization-Id`string

Selects which of the caller's organizations the request acts on. When omitted, the caller's default organization is used.

Body parameters

`filename`stringrequired

Must end in .apk or .ipa.

`filesize`integer · int64required

Bytes. At most the plan's per-file cap: hobby 250 MB, starter 1 GB, team and enterprise 2 GB.

`sha256`stringrequired

Lowercase hex SHA-256 of the file.

`expiresIn`integer · int64

Seconds until the file expires: 10 minutes to 90 days, default 30 days. A dedup hit only ever extends the expiry.

`name`string

Display name. Kept from the first create on a dedup hit.

`build`object

Where the build came from. Every field is optional; CI clients fill it from their environment.

Show child attributes

`branch`string

`commit`string

`ciRunUrl`string · uri

https only. Stored and displayed, never fetched.

Returns

`id`string · uuidrequired

`status`stringrequired

pendingUpload: bytes not verified yet. processing: verification running (seen only by a concurrent caller). ready: installable. failed: see failureReason; creating the same sha256 again resets it.

`app`string · uuidrequirednullable

The app this file belongs to, once metadata is extracted.

`platform`stringrequirednullable

Known once metadata is extracted.

`filename`stringrequired

`filesize`integer · int64required

`sha256`stringrequired

`name`stringrequired

Display name. Defaults to the filename until metadata is extracted.

`bundleId`stringrequirednullable

Android package name or iOS bundle identifier.

`version`stringrequirednullable

Version name (Android) or CFBundleShortVersionString (iOS).

`buildNumber`stringrequirednullable

Version code (Android) or CFBundleVersion (iOS).

`minOsVersion`stringrequirednullable

Minimum OS version the build supports.

`isSimulator`booleanrequired

True for an iOS simulator build.

`build`objectrequirednullable

Show child attributes

`branch`string

`commit`string

`ciRunUrl`string · uri

https only. Stored and displayed, never fetched.

`source`stringrequired

How the file was created, derived from the credential.

`organization`stringrequired

Organization that owns the file.

`user`stringrequirednullable

Uploader; null if the user was deleted.

`failureReason`stringrequirednullable

Why the file failed; null otherwise.

`createdAt`string · date-timerequired

`lastUsedAt`string · date-timerequirednullable

Last dedup hit or install.

`expiresAt`string · date-timerequired

The file and its bytes are deleted after this.

`upload`object

Present only while the caller is expected to upload the bytes. Send exactly these headers; the URL is signed for the declared size and SHA-256, so any other bytes are rejected by storage.

Show child attributes

`method`stringrequired

`url`string · urirequired

`headers`objectrequired

`expiresAt`string · date-timerequired

When the URL stops working. Calling create again returns a fresh one.

Request

```
curl https://app.mobilenext.ai/api/v1/files \

  -X POST \

  -H 'Authorization: Bearer mob_...' \

  -H 'Content-Type: application/json' \

  -d '{"filename":"app-release.apk","filesize":48211033,"sha256":"string","expiresIn":2592000,"name":"string","build":{"branch":"fix/checkout","commit":"3dbd301","ciRunUrl":"https://github.com/acme/shop/actions/runs/991"}}'
```

Response

200201400401403409413422429

The organization already stores this sha256; \`upload\` is present only when the bytes are still needed

```
{

  "id": "string",

  "status": "pendingUpload",

  "app": "string",

  "platform": "android",

  "filename": "app-release.apk",

  "filesize": 48211033,

  "sha256": "string",

  "name": "Checkout fix",

  "bundleId": "com.acme.shop",

  "version": "4.2.0",

  "buildNumber": "4201",

  "minOsVersion": "8.0",

  "isSimulator": false,

  "build": {

    "branch": "fix/checkout",

    "commit": "3dbd301",

    "ciRunUrl": "https://github.com/acme/shop/actions/runs/991"

  },

  "source": "api",

  "organization": "string",

  "user": "string",

  "failureReason": "checksum_mismatch",

  "createdAt": "2026-01-01T00:00:00Z",

  "lastUsedAt": "2026-01-01T00:00:00Z",

  "expiresAt": "2026-01-01T00:00:00Z",

  "upload": {

    "method": "PUT",

    "url": "string",

    "headers": {

      "X-Amz-Checksum-Sha256": "WJG1tSLV3whtD/CxEPvZ0hu0/HFjrzTQgoai6Eb2vgM=",

      "Content-Length": "48211033"

    },

    "expiresAt": "2026-01-01T00:00:00Z"

  }

}
```

## Get file storage usage[​](#get-file-usage "Direct link to Get file storage usage")

GET/api/v1/files/usage

Stored bytes against the plan's quota, for display. The quota is not enforced yet; the only enforced limit is a safety ceiling (`403 storage_quota_exceeded` on create).

Header parameters

`X-Organization-Id`string

Selects which of the caller's organizations the request acts on. When omitted, the caller's default organization is used.

Returns

`usedBytes`integer · int64required

Sum of the organization's stored files, failed files excluded.

`quotaBytes`integer · int64requirednullable

The plan's storage quota; null when unlimited. Shown, not enforced yet.

Request

```
curl https://app.mobilenext.ai/api/v1/files/usage \

  -X GET \

  -H 'Authorization: Bearer mob_...'
```

Response

200401

Usage

```
{

  "usedBytes": 0,

  "quotaBytes": 0

}
```

## Get a file[​](#get-file "Direct link to Get a file")

GET/api/v1/files/{fileId}

Returns a file in any status. A missing, expired or other organization's file is 404.

Path parameters

`fileId`string · uuidrequired

File UUID

Header parameters

`X-Organization-Id`string

Selects which of the caller's organizations the request acts on. When omitted, the caller's default organization is used.

Returns

One stored app binary. Immutable bytes, unique per organization by SHA-256.

`id`string · uuidrequired

`status`stringrequired

pendingUpload: bytes not verified yet. processing: verification running (seen only by a concurrent caller). ready: installable. failed: see failureReason; creating the same sha256 again resets it.

`app`string · uuidrequirednullable

The app this file belongs to, once metadata is extracted.

`platform`stringrequirednullable

Known once metadata is extracted.

`filename`stringrequired

`filesize`integer · int64required

`sha256`stringrequired

`name`stringrequired

Display name. Defaults to the filename until metadata is extracted.

`bundleId`stringrequirednullable

Android package name or iOS bundle identifier.

`version`stringrequirednullable

Version name (Android) or CFBundleShortVersionString (iOS).

`buildNumber`stringrequirednullable

Version code (Android) or CFBundleVersion (iOS).

`minOsVersion`stringrequirednullable

Minimum OS version the build supports.

`isSimulator`booleanrequired

True for an iOS simulator build.

`build`objectrequirednullable

Show child attributes

`branch`string

`commit`string

`ciRunUrl`string · uri

https only. Stored and displayed, never fetched.

`source`stringrequired

How the file was created, derived from the credential.

`organization`stringrequired

Organization that owns the file.

`user`stringrequirednullable

Uploader; null if the user was deleted.

`failureReason`stringrequirednullable

Why the file failed; null otherwise.

`createdAt`string · date-timerequired

`lastUsedAt`string · date-timerequirednullable

Last dedup hit or install.

`expiresAt`string · date-timerequired

The file and its bytes are deleted after this.

Request

```
curl https://app.mobilenext.ai/api/v1/files/{fileId} \

  -X GET \

  -H 'Authorization: Bearer mob_...'
```

Response

200401404

The file

```
{

  "id": "string",

  "status": "pendingUpload",

  "app": "string",

  "platform": "android",

  "filename": "app-release.apk",

  "filesize": 48211033,

  "sha256": "string",

  "name": "Checkout fix",

  "bundleId": "com.acme.shop",

  "version": "4.2.0",

  "buildNumber": "4201",

  "minOsVersion": "8.0",

  "isSimulator": false,

  "build": {

    "branch": "fix/checkout",

    "commit": "3dbd301",

    "ciRunUrl": "https://github.com/acme/shop/actions/runs/991"

  },

  "source": "api",

  "organization": "string",

  "user": "string",

  "failureReason": "checksum_mismatch",

  "createdAt": "2026-01-01T00:00:00Z",

  "lastUsedAt": "2026-01-01T00:00:00Z",

  "expiresAt": "2026-01-01T00:00:00Z"

}
```

## Update a file[​](#update-file "Direct link to Update a file")

PATCH/api/v1/files/{fileId}

Changes a file's expiry or display name. The bytes are immutable.

Path parameters

`fileId`string · uuidrequired

File UUID

Header parameters

`X-Organization-Id`string

Selects which of the caller's organizations the request acts on. When omitted, the caller's default organization is used.

Body parameters

`expiresIn`integer · int64

Seconds from now: 10 minutes to 90 days. Restarts the clock and may shorten the expiry (a create never does).

`name`string

Display name. An empty string clears it, so the file shows its filename again.

Returns

One stored app binary. Immutable bytes, unique per organization by SHA-256.

`id`string · uuidrequired

`status`stringrequired

pendingUpload: bytes not verified yet. processing: verification running (seen only by a concurrent caller). ready: installable. failed: see failureReason; creating the same sha256 again resets it.

`app`string · uuidrequirednullable

The app this file belongs to, once metadata is extracted.

`platform`stringrequirednullable

Known once metadata is extracted.

`filename`stringrequired

`filesize`integer · int64required

`sha256`stringrequired

`name`stringrequired

Display name. Defaults to the filename until metadata is extracted.

`bundleId`stringrequirednullable

Android package name or iOS bundle identifier.

`version`stringrequirednullable

Version name (Android) or CFBundleShortVersionString (iOS).

`buildNumber`stringrequirednullable

Version code (Android) or CFBundleVersion (iOS).

`minOsVersion`stringrequirednullable

Minimum OS version the build supports.

`isSimulator`booleanrequired

True for an iOS simulator build.

`build`objectrequirednullable

Show child attributes

`branch`string

`commit`string

`ciRunUrl`string · uri

https only. Stored and displayed, never fetched.

`source`stringrequired

How the file was created, derived from the credential.

`organization`stringrequired

Organization that owns the file.

`user`stringrequirednullable

Uploader; null if the user was deleted.

`failureReason`stringrequirednullable

Why the file failed; null otherwise.

`createdAt`string · date-timerequired

`lastUsedAt`string · date-timerequirednullable

Last dedup hit or install.

`expiresAt`string · date-timerequired

The file and its bytes are deleted after this.

Request

```
curl https://app.mobilenext.ai/api/v1/files/{fileId} \

  -X PATCH \

  -H 'Authorization: Bearer mob_...' \

  -H 'Content-Type: application/json' \

  -d '{"expiresIn":0,"name":"string"}'
```

Response

200400401403404422

The updated file

```
{

  "id": "string",

  "status": "pendingUpload",

  "app": "string",

  "platform": "android",

  "filename": "app-release.apk",

  "filesize": 48211033,

  "sha256": "string",

  "name": "Checkout fix",

  "bundleId": "com.acme.shop",

  "version": "4.2.0",

  "buildNumber": "4201",

  "minOsVersion": "8.0",

  "isSimulator": false,

  "build": {

    "branch": "fix/checkout",

    "commit": "3dbd301",

    "ciRunUrl": "https://github.com/acme/shop/actions/runs/991"

  },

  "source": "api",

  "organization": "string",

  "user": "string",

  "failureReason": "checksum_mismatch",

  "createdAt": "2026-01-01T00:00:00Z",

  "lastUsedAt": "2026-01-01T00:00:00Z",

  "expiresAt": "2026-01-01T00:00:00Z"

}
```

## Delete a file[​](#delete-file "Direct link to Delete a file")

DELETE/api/v1/files/{fileId}

Deletes the file immediately: it no longer appears in lists, cannot be fetched or downloaded, and stops counting as usage. Download URLs already issued keep working until they expire.

Path parameters

`fileId`string · uuidrequired

File UUID

Header parameters

`X-Organization-Id`string

Selects which of the caller's organizations the request acts on. When omitted, the caller's default organization is used.

Request

```
curl https://app.mobilenext.ai/api/v1/files/{fileId} \

  -X DELETE \

  -H 'Authorization: Bearer mob_...'
```

Response

204401403404

Deleted

```
// No response body
```

## Complete a file upload[​](#complete-file "Direct link to Complete a file upload")

POST/api/v1/files/{fileId}/complete

Call after the PUT. Verifies the stored bytes against the declared size and SHA-256 and returns the file as `ready` or `failed`. Synchronous; there is nothing to poll. Idempotent: a `ready` or `failed` file is returned unchanged.

Path parameters

`fileId`string · uuidrequired

File UUID

Header parameters

`X-Organization-Id`string

Selects which of the caller's organizations the request acts on. When omitted, the caller's default organization is used.

Returns

One stored app binary. Immutable bytes, unique per organization by SHA-256.

`id`string · uuidrequired

`status`stringrequired

pendingUpload: bytes not verified yet. processing: verification running (seen only by a concurrent caller). ready: installable. failed: see failureReason; creating the same sha256 again resets it.

`app`string · uuidrequirednullable

The app this file belongs to, once metadata is extracted.

`platform`stringrequirednullable

Known once metadata is extracted.

`filename`stringrequired

`filesize`integer · int64required

`sha256`stringrequired

`name`stringrequired

Display name. Defaults to the filename until metadata is extracted.

`bundleId`stringrequirednullable

Android package name or iOS bundle identifier.

`version`stringrequirednullable

Version name (Android) or CFBundleShortVersionString (iOS).

`buildNumber`stringrequirednullable

Version code (Android) or CFBundleVersion (iOS).

`minOsVersion`stringrequirednullable

Minimum OS version the build supports.

`isSimulator`booleanrequired

True for an iOS simulator build.

`build`objectrequirednullable

Show child attributes

`branch`string

`commit`string

`ciRunUrl`string · uri

https only. Stored and displayed, never fetched.

`source`stringrequired

How the file was created, derived from the credential.

`organization`stringrequired

Organization that owns the file.

`user`stringrequirednullable

Uploader; null if the user was deleted.

`failureReason`stringrequirednullable

Why the file failed; null otherwise.

`createdAt`string · date-timerequired

`lastUsedAt`string · date-timerequirednullable

Last dedup hit or install.

`expiresAt`string · date-timerequired

The file and its bytes are deleted after this.

Request

```
curl https://app.mobilenext.ai/api/v1/files/{fileId}/complete \

  -X POST \

  -H 'Authorization: Bearer mob_...'
```

Response

200401403404409

The file, \`ready\` or \`failed\`

```
{

  "id": "string",

  "status": "pendingUpload",

  "app": "string",

  "platform": "android",

  "filename": "app-release.apk",

  "filesize": 48211033,

  "sha256": "string",

  "name": "Checkout fix",

  "bundleId": "com.acme.shop",

  "version": "4.2.0",

  "buildNumber": "4201",

  "minOsVersion": "8.0",

  "isSimulator": false,

  "build": {

    "branch": "fix/checkout",

    "commit": "3dbd301",

    "ciRunUrl": "https://github.com/acme/shop/actions/runs/991"

  },

  "source": "api",

  "organization": "string",

  "user": "string",

  "failureReason": "checksum_mismatch",

  "createdAt": "2026-01-01T00:00:00Z",

  "lastUsedAt": "2026-01-01T00:00:00Z",

  "expiresAt": "2026-01-01T00:00:00Z"

}
```

## Download a file[​](#download-file "Direct link to Download a file")

GET/api/v1/files/{fileId}/download

Returns a presigned download URL valid for 5 minutes; it saves under the file's original filename. Any role. Does not change `lastUsedAt`. Returns JSON rather than redirecting, so the route itself cannot be shared as a download link.

Path parameters

`fileId`string · uuidrequired

File UUID

Header parameters

`X-Organization-Id`string

Selects which of the caller's organizations the request acts on. When omitted, the caller's default organization is used.

Returns

`url`string · urirequired

Presigned download URL; the browser saves it under the file's original filename.

`expiresAt`string · date-timerequired

When the URL stops working (5 minutes).

Request

```
curl https://app.mobilenext.ai/api/v1/files/{fileId}/download \

  -X GET \

  -H 'Authorization: Bearer mob_...'
```

Response

200401404409

A short-lived download URL

```
{

  "url": "string",

  "expiresAt": "2026-01-01T00:00:00Z"

}
```
