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

# API Reference

> Complete API documentation for file upload endpoints

## Authentication

All upload endpoints require authentication via tRPC context. The user must be logged in.

## Simple Upload

### requestUploadUrl

Request a presigned URL for direct file upload.

<ParamField body="filename" type="string" required>
  Name of the file being uploaded
</ParamField>

<ParamField body="mimeType" type="string" required>
  MIME type of the file (e.g., `image/jpeg`, `video/mp4`)
</ParamField>

<ParamField body="size" type="number" required>
  File size in bytes
</ParamField>

**Response:**

```typescript theme={null}
{
  uploadUrl: string; // Presigned S3 URL for PUT request
  fileId: string; // Database file record ID
  key: string; // S3 object key
}
```

**Example:**

```typescript theme={null}
const { uploadUrl, fileId } = await trpc.upload.requestUploadUrl.mutate({
  filename: "photo.jpg",
  mimeType: "image/jpeg",
  size: 1024000,
});

// Upload directly to S3
await fetch(uploadUrl, {
  method: "PUT",
  headers: { "Content-Type": "image/jpeg" },
  body: fileBlob,
});
```

***

### confirmUpload

Mark a file as successfully uploaded.

<ParamField body="fileId" type="string" required>
  File record ID from `requestUploadUrl`
</ParamField>

**Response:**

```typescript theme={null}
{
  success: boolean;
  file: {
    id: string;
    filename: string;
    url: string;
  }
}
```

***

## Multipart Upload

For files larger than 10MB, use multipart upload for resumability.

### initiateMultipart

Start a new multipart upload.

<ParamField body="filename" type="string" required>
  Name of the file
</ParamField>

<ParamField body="mimeType" type="string" required>
  MIME type of the file
</ParamField>

<ParamField body="size" type="number" required>
  Total file size in bytes
</ParamField>

<ParamField body="totalParts" type="number" required>
  Number of parts the file will be split into
</ParamField>

**Response:**

```typescript theme={null}
{
  fileId: string; // Database file record ID
  uploadId: string; // S3 multipart upload ID
  key: string; // S3 object key
}
```

***

### getPartUrl

Get presigned URL for uploading a specific part.

<ParamField body="fileId" type="string" required>
  File record ID
</ParamField>

<ParamField body="partNumber" type="number" required>
  Part number (1-indexed)
</ParamField>

**Response:**

```typescript theme={null}
{
  uploadUrl: string; // Presigned URL for this part
}
```

***

### completePart

Record that a part has been uploaded successfully.

<ParamField body="fileId" type="string" required>
  File record ID
</ParamField>

<ParamField body="partNumber" type="number" required>
  Part number that was uploaded
</ParamField>

<ParamField body="etag" type="string" required>
  ETag returned from S3 after uploading the part
</ParamField>

**Response:**

```typescript theme={null}
{
  success: boolean;
  uploadedParts: number[];  // Array of completed part numbers
}
```

***

### completeMultipart

Finalize the multipart upload after all parts are uploaded.

<ParamField body="fileId" type="string" required>
  File record ID
</ParamField>

**Response:**

```typescript theme={null}
{
  success: boolean;
  file: {
    id: string;
    filename: string;
    url: string;
  }
}
```

***

### abortMultipart

Cancel a multipart upload and clean up parts.

<ParamField body="fileId" type="string" required>
  File record ID
</ParamField>

**Response:**

```typescript theme={null}
{
  success: boolean;
}
```

***

## Error Codes

| Code                    | Description                 |
| ----------------------- | --------------------------- |
| `UNAUTHORIZED`          | User not authenticated      |
| `BAD_REQUEST`           | Invalid file type or size   |
| `QUOTA_EXCEEDED`        | User storage quota exceeded |
| `NOT_FOUND`             | File record not found       |
| `INTERNAL_SERVER_ERROR` | S3 or database error        |

## Allowed MIME Types

```typescript theme={null}
const ALLOWED_MIME_TYPES = [
  "image/jpeg",
  "image/png",
  "image/gif",
  "image/webp",
  "application/pdf",
  "video/mp4",
  "video/quicktime",
];
```

To add more types, modify `ALLOWED_MIME_TYPES` in `apps/api/src/routers/upload.ts`.

## Size Limits

| Limit               | Value  |
| ------------------- | ------ |
| Simple upload max   | 10 MB  |
| Multipart part size | 5 MB   |
| Maximum file size   | 5 GB   |
| Default user quota  | 500 MB |

## Test Checklist

* `requestUploadUrl` returns a presigned URL
* Upload succeeds and `confirmUpload` marks the file as uploaded
* Multipart uploads complete successfully

## Troubleshooting

If you see `BAD_REQUEST` or `QUOTA_EXCEEDED`, confirm allowed MIME types and
storage quota configuration.

## Remove / Disable

To disable uploads while you configure S3, set:

`apps/mobile/features/feature-registry.tsx` → `featureFlags.fileUploads = false`

For production removal guidance, see [Removing Features](/essentials/removing-features).
