> ## Documentation Index
> Fetch the complete documentation index at: https://api-docs.nunu.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Initiate Upload

> Initiate a build upload (single-part or multipart).

<Info>
  **Permission Required**: `project:manage-build-storage`
</Info>

Initiate a build upload. The API automatically selects single-part or multipart upload based on file size.

<Tip>
  **For most users**, we recommend using the [CLI](/ci-cd-integration/build-storage/quick-start) or [GitHub Action](/ci-cd-integration/build-storage/integrations/github-action) instead of the API directly.
</Tip>

## Upload Methods

| Method          | File Size         | Use Case                      |
| --------------- | ----------------- | ----------------------------- |
| **Single-part** | \< 3GB            | Simple uploads                |
| **Multipart**   | ≥ 3GB (up to 5TB) | Large files, parallel uploads |

The API automatically uses multipart for files ≥3GB unless you explicitly set `multipart: false`.

## Request Body

<ParamField body="project_id" type="string">
  Optional. The project is taken from the URL. If provided, it must match the projectId in the route.
</ParamField>

<ParamField body="name" type="string" required>
  Build name (1-255 characters)
</ParamField>

<ParamField body="file_name" type="string" required>
  File name (1-255 characters)
</ParamField>

<ParamField body="file_size" type="integer" required>
  Size in bytes (positive integer)
</ParamField>

<ParamField body="platform" type="string" required>
  Target platform: `windows`, `macos`, `linux`, `android`, `ios-native`, `ios-simulator`, `xbox`, `playstation`
</ParamField>

<ParamField body="description" type="string">
  Description (max 1000 characters)
</ParamField>

<ParamField body="multipart" type="boolean">
  Force multipart upload. Auto-enabled for files ≥3GB.
</ParamField>

<ParamField body="auto_delete" type="boolean" default="false">
  Auto-delete old builds when storage capacity is reached
</ParamField>

<ParamField body="deletion_policy" type="string" default="least_recent">
  Deletion policy: `least_recent` (LRU) or `oldest` (FIFO)
</ParamField>

<ParamField body="upload_timeout" type="integer">
  Timeout in minutes (1-1440). Default: 10 for single-part, 60 for multipart.
</ParamField>

<ParamField body="tags" type="string[]">
  Array of tags (1-50 characters each)
</ParamField>

<ParamField body="details" type="object">
  Build metadata (VCS, CI, app info)

  <Expandable title="details properties">
    <ParamField body="details.vcs" type="object">
      Version control metadata

      <Expandable title="vcs properties">
        <ParamField body="details.vcs.type" type="string">
          VCS type: `git`, `mercurial`, `svn`
        </ParamField>

        <ParamField body="details.vcs.provider" type="string">
          Provider: `github`, `gitlab`, `bitbucket`, `azure-devops`
        </ParamField>

        <ParamField body="details.vcs.branch" type="string">
          Branch name
        </ParamField>

        <ParamField body="details.vcs.commit" type="object">
          Commit info (hash, short\_hash, message, author, timestamp)
        </ParamField>
      </Expandable>
    </ParamField>

    <ParamField body="details.ci" type="object">
      CI/CD metadata (system, build\_number, job\_name, run\_url)
    </ParamField>

    <ParamField body="details.app" type="object">
      App metadata (version, version\_code, build\_number, bundle\_id)
    </ParamField>
  </Expandable>
</ParamField>

## Response (Single-Part)

<ResponseField name="upload_type" type="string" required>
  Always `single-part`
</ResponseField>

<ResponseField name="build_id" type="string" required>
  Build ID for completing the upload
</ResponseField>

<ResponseField name="upload_url" type="string" required>
  Presigned URL to PUT the file
</ResponseField>

<ResponseField name="object_key" type="string" required>
  Object key for the upload
</ResponseField>

## Response (Multipart)

<ResponseField name="upload_type" type="string" required>
  Always `multipart`
</ResponseField>

<ResponseField name="build_id" type="string" required>
  Build ID for completing the upload
</ResponseField>

<ResponseField name="upload_id" type="string" required>
  Upload ID for getting part URLs
</ResponseField>

<ResponseField name="object_key" type="string" required>
  Object key for the upload
</ResponseField>

<ResponseField name="total_parts" type="integer" required>
  Total number of parts to upload
</ResponseField>

<ResponseField name="part_size" type="integer" required>
  Size of each part in bytes (typically 100MB)
</ResponseField>

<RequestExample>
  ```bash cURL theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  curl -X POST "https://nunu.ai/api/v1/project/your-project-id/builds/upload" \
    -H "X-Api-Key: YOUR_API_TOKEN" \
    -H "Content-Type: application/json" \
    -d '{
      "project_id": "your-project-id",
      "name": "Production Build v1.2.3",
      "file_name": "app-release.apk",
      "file_size": 52428800,
      "platform": "android",
      "multipart": false,
      "auto_delete": true,
      "tags": ["version:1.2.3", "env:production"],
      "details": {
        "vcs": {
          "commit": { "short_hash": "a1b2c3d" },
          "branch": "main"
        }
      }
    }'
  ```

  ```typescript JavaScript theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  const response = await fetch("https://nunu.ai/api/v1/project/your-project-id/builds/upload", {
    method: "POST",
    headers: {
      "X-Api-Key": process.env.NUNU_API_TOKEN,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      project_id: "your-project-id",
      name: "Production Build v1.2.3",
      file_name: "app-release.apk",
      file_size: 52428800,
      platform: "android",
      multipart: false,
    }),
  });
  const data = await response.json();
  ```

  ```python Python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  import requests
  import os

  response = requests.post(
      "https://nunu.ai/api/v1/project/your-project-id/builds/upload",
      headers={
          "X-Api-Key": os.environ["NUNU_API_TOKEN"],
          "Content-Type": "application/json"
      },
      json={
          "project_id": "your-project-id",
          "name": "Production Build v1.2.3",
          "file_name": "app-release.apk",
          "file_size": 52428800,
          "platform": "android",
          "multipart": False
      }
  )
  data = response.json()
  ```
</RequestExample>

<ResponseExample>
  ```json Single-Part theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  {
    "upload_type": "single-part",
    "build_id": "123e4567-e89b-12d3-a456-426614174000",
    "upload_url": "https://storage.example.com/...",
    "object_key": "abc123-def456/app-release.apk"
  }
  ```

  ```json Multipart theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  {
    "upload_type": "multipart",
    "build_id": "123e4567-e89b-12d3-a456-426614174000",
    "upload_id": "ExampleUploadId123",
    "object_key": "abc123-def456/large-app.apk",
    "total_parts": 52,
    "part_size": 104857600
  }
  ```

  ```json 402 Quota Exceeded theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  {
    "error": "Storage capacity exceeded",
    "code": "QUOTA_EXCEEDED",
    "details": {
      "used": 10737418240,
      "capacity": 10737418240,
      "needed": 524288000
    }
  }
  ```
</ResponseExample>

***

## Upload Flow

<Tabs>
  <Tab title="Single-Part Upload">
    <Steps>
      <Step title="Initiate">
        POST to `/builds/upload` with `multipart: false`
      </Step>

      <Step title="Upload">
        PUT the file to the returned `upload_url`
      </Step>

      <Step title="Complete">
        POST to `/builds/upload/complete`
      </Step>
    </Steps>
  </Tab>

  <Tab title="Multipart Upload">
    <Steps>
      <Step title="Initiate">
        POST to `/builds/upload` (automatic for files ≥3GB)
      </Step>

      <Step title="Get Part URLs">
        GET `/builds/upload/parts` for presigned URLs
      </Step>

      <Step title="Upload Parts">
        PUT each part to its URL, save ETags
      </Step>

      <Step title="Complete">
        POST to `/builds/upload/complete` with ETags
      </Step>
    </Steps>
  </Tab>
</Tabs>

## Complete Single-Part Example

<CodeGroup>
  ```bash Shell Script theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  #!/bin/bash
  PROJECT_ID="your-project-id"
  API_TOKEN="your-api-token"
  FILE_PATH="build/app.apk"

  # Step 1: Initiate
  RESPONSE=$(curl -s -X POST \
    "https://nunu.ai/api/v1/project/${PROJECT_ID}/builds/upload" \
    -H "Content-Type: application/json" \
    -H "X-Api-Key: $API_TOKEN" \
    -d "{
      \"project_id\": \"$PROJECT_ID\",
      \"name\": \"Production Build\",
      \"file_name\": \"$(basename $FILE_PATH)\",
      \"file_size\": $(stat -f%z $FILE_PATH 2>/dev/null || stat -c%s $FILE_PATH),
      \"platform\": \"android\",
      \"multipart\": false
    }")

  UPLOAD_URL=$(echo $RESPONSE | jq -r '.upload_url')
  BUILD_ID=$(echo $RESPONSE | jq -r '.build_id')

  # Step 2: Upload
  curl -X PUT "$UPLOAD_URL" \
    -H "Content-Type: application/octet-stream" \
    --data-binary "@$FILE_PATH"

  # Step 3: Complete
  curl -X POST \
    "https://nunu.ai/api/v1/project/${PROJECT_ID}/builds/upload/complete" \
    -H "Content-Type: application/json" \
    -H "X-Api-Key: $API_TOKEN" \
    -d "{\"build_id\": \"$BUILD_ID\"}"

  echo "Upload complete! Build ID: $BUILD_ID"
  ```

  ```typescript JavaScript theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  async function uploadBuild(projectId: string, file: File, name: string, platform: string) {
    const token = process.env.NUNU_API_TOKEN;

    // Step 1: Initiate
    const initRes = await fetch(`https://nunu.ai/api/v1/project/${projectId}/builds/upload`, {
      method: "POST",
      headers: { "Content-Type": "application/json", "X-Api-Key": token },
      body: JSON.stringify({
        project_id: projectId,
        name,
        file_name: file.name,
        file_size: file.size,
        platform,
        multipart: false,
      }),
    });
    const { upload_url, build_id } = await initRes.json();

    // Step 2: Upload
    await fetch(upload_url, {
      method: "PUT",
      body: file,
      headers: { "Content-Type": "application/octet-stream" },
    });

    // Step 3: Complete
    await fetch(`https://nunu.ai/api/v1/project/${projectId}/builds/upload/complete`, {
      method: "POST",
      headers: { "Content-Type": "application/json", "X-Api-Key": token },
      body: JSON.stringify({ build_id }),
    });

    return build_id;
  }
  ```

  ```python Python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  import requests
  import os

  def upload_build(project_id: str, file_path: str, name: str, platform: str) -> str:
      token = os.environ["NUNU_API_TOKEN"]
      file_size = os.path.getsize(file_path)
      
      # Step 1: Initiate
      init_res = requests.post(
          f"https://nunu.ai/api/v1/project/{project_id}/builds/upload",
          headers={"Content-Type": "application/json", "X-Api-Key": token},
          json={
              "project_id": project_id,
              "name": name,
              "file_name": os.path.basename(file_path),
              "file_size": file_size,
              "platform": platform,
              "multipart": False
          }
      )
      data = init_res.json()
      
      # Step 2: Upload
      with open(file_path, "rb") as f:
          requests.put(data["upload_url"], data=f, 
                       headers={"Content-Type": "application/octet-stream"})
      
      # Step 3: Complete
      requests.post(
          f"https://nunu.ai/api/v1/project/{project_id}/builds/upload/complete",
          headers={"Content-Type": "application/json", "X-Api-Key": token},
          json={"build_id": data["build_id"]}
      )
      
      return data["build_id"]
  ```
</CodeGroup>


## OpenAPI

````yaml POST /project/{projectId}/builds/upload
openapi: 3.1.0
info:
  title: nunu.ai API
  description: >-
    The nunu.ai Public API allows you to programmatically interact with your
    projects, manage test runs, and upload builds.
  version: 1.0.0
servers:
  - url: https://nunu.ai/api/v1
security:
  - apiKeyAuth: []
paths:
  /project/{projectId}/builds/upload:
    parameters:
      - name: projectId
        in: path
        required: true
        schema:
          type: string
        description: >-
          The project ID. You can copy it from the project settings page or the
          project URL in the dashboard.
    post:
      tags:
        - Builds
      summary: Initiate Upload
      description: Initiate a build upload (single-part or multipart).
      operationId: initiateUpload
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/InitiateUploadRequest'
      responses:
        '200':
          description: Upload initiated successfully
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: '#/components/schemas/SinglePartUploadResponse'
                  - $ref: '#/components/schemas/MultipartUploadResponse'
        '400':
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '402':
          description: Storage capacity exceeded
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
components:
  schemas:
    InitiateUploadRequest:
      type: object
      properties:
        project_id:
          type: string
          description: >-
            Optional. The project is taken from the URL. If provided, it must
            match the projectId in the route.
        name:
          type: string
          description: Build name (1-255 chars)
        file_name:
          type: string
          description: File name (1-255 chars)
        file_size:
          type: integer
          description: Size in bytes (positive integer)
        platform:
          type: string
          enum:
            - windows
            - macos
            - linux
            - android
            - ios-native
            - ios-simulator
            - xbox
            - playstation
          description: Target platform
        description:
          type: string
          description: Description (max 1000 chars)
        multipart:
          type: boolean
          description: Use multipart upload? (Auto for files ≥3GB)
        auto_delete:
          type: boolean
          default: false
          description: Auto-delete old builds when capacity reached
        deletion_policy:
          type: string
          enum:
            - least_recent
            - oldest
          default: least_recent
        upload_timeout:
          type: integer
          description: Timeout in minutes (1-1440)
        tags:
          type: array
          items:
            type: string
          description: Array of tags (1-50 chars each)
        details:
          $ref: '#/components/schemas/BuildDetails'
      required:
        - name
        - file_name
        - file_size
        - platform
    SinglePartUploadResponse:
      type: object
      properties:
        upload_type:
          type: string
          const: single-part
        build_id:
          type: string
        upload_url:
          type: string
        object_key:
          type: string
    MultipartUploadResponse:
      type: object
      properties:
        upload_type:
          type: string
          const: multipart
        build_id:
          type: string
        upload_id:
          type: string
        object_key:
          type: string
        total_parts:
          type: integer
        part_size:
          type: integer
    Error:
      type: object
      properties:
        error:
          type: string
        code:
          type: string
        details:
          type: object
    BuildDetails:
      type: object
      description: Optional build metadata
      properties:
        vcs:
          type: object
          properties:
            type:
              type: string
              enum:
                - git
                - mercurial
                - svn
            provider:
              type: string
              enum:
                - github
                - gitlab
                - bitbucket
                - azure-devops
            repository_url:
              type: string
            commit:
              type: object
              properties:
                hash:
                  type: string
                short_hash:
                  type: string
                message:
                  type: string
                author:
                  type: string
                timestamp:
                  type: string
            branch:
              type: string
            tag:
              type: string
            pr:
              type: object
              properties:
                number:
                  type: integer
                title:
                  type: string
                url:
                  type: string
                source_branch:
                  type: string
                target_branch:
                  type: string
        ci:
          type: object
          properties:
            system:
              type: string
              enum:
                - github-actions
                - jenkins
                - gitlab-ci
                - circleci
                - travis
                - azure-pipelines
                - bitrise
                - custom
            build_number:
              type: string
            job_name:
              type: string
            run_id:
              type: string
            run_url:
              type: string
            triggered_by:
              type: string
            agent:
              type: string
        app:
          type: object
          properties:
            version:
              type: string
            version_code:
              type: integer
            build_number:
              type: string
            bundle_id:
              type: string
        build:
          type: object
          properties:
            configuration:
              type: string
              enum:
                - debug
                - release
                - staging
                - production
            timestamp:
              type: string
            duration:
              type: integer
            compiler:
              type: string
            dependencies:
              type: object
        upload:
          type: object
          properties:
            method:
              type: string
              enum:
                - cli
                - api
                - github-action
                - jenkins-plugin
                - web
            cli_version:
              type: string
            uploader:
              type: string
  securitySchemes:
    apiKeyAuth:
      type: apiKey
      in: header
      name: X-Api-Key
      description: API key for authentication

````