# Uploading files with the File Storage API

The Apideck [File Storage API](/apis/file-storage/reference) supports two ways to upload files. The [direct file upload API](/apis/file-storage/reference#operation/filesUpload) supports files up to 100MB in size and sends all the binary file data in a single API call.

The [upload sessions API](/apis/file-storage/reference#operation/uploadSessionsAdd) allows you to upload files of any size in parts. Note that some file storage connectors have their own limits.

> **Note 🚨**
>
> File upload API calls use `https://upload.apideck.com` as the base URL.

## Direct upload

To upload a file via direct upload, make an API call to the [`POST /file-storage/files`](/apis/file-storage/reference#operation/filesUpload) endpoint with the content of the file, the file `name` and `parent_folder_id`.

### Request body

The request body should be the raw binary content of the file. Do not use form data.

### File metadata

Provide the file's `name` and `parent_folder_id` as JSON in the `x-apideck-metadata` header.

```sh meta=terminal
curl  --request POST \
      --url https://upload.apideck.com/file-storage/files \
      --header 'Authorization: Bearer API_KEY' \
      --header 'Content-Type: video/mp4' \
      --header 'x-apideck-app-id: APP_ID' \
      --header 'x-apideck-consumer-id: CONSUMER_ID' \
      --header 'x-apideck-metadata: {"name": "video.mp4", "parent_folder_id": "root"}' \
      --header 'x-apideck-service-id: google-drive' \
      --data-binary @sample_hd.mp4
```

## Upload sessions

The upload sessions API requires you to make multiple API calls:

1. [Create upload session](#1-create-upload-session)
2. [Get upload session](#2-get-upload-session)
3. [Upload parts of file](#3-upload-parts-of-file)
4. [Finish upload session](#4-finish-upload-session)

> **Note 🚨**
>
> Our Node.js SDK includes a utility to handle file uploads. You can find it{' '}
> <Link href="https://github.com/apideck-libraries/node-sdk/blob/main/src/utils.ts#L67-L123">
> here
> </Link>

### 1. Create upload session

To create an upload session, make an API call to the [`POST /file-storage/upload-sessions`](/apis/file-storage/reference#operation/uploadSessionsAdd) endpoint with the file `name`, file `size` and `parent_folder_id`.

```sh meta=terminal
curl  --request POST \
      --url https://upload.apideck.com/file-storage/upload-sessions \
      --header 'Authorization: Bearer API_KEY' \
      --header 'Content-Type: application/json' \
      --header 'x-apideck-app-id: APP_ID' \
      --header 'x-apideck-consumer-id: CONSUMER_ID' \
      --header 'x-apideck-service-id: google-drive' \
      --data '{"size":30228718,"name": "video.mp4","parent_folder_id": "root"}'
```

### 2. Get upload session

After creating an upload session, make an API call to [`GET /file-storage/upload-sessions/ID`](/apis/file-storage/reference#operation/uploadSessionsOne). Use the ID you received after creating the upload session.

```sh meta=terminal
curl  --request GET \
      --url https://upload.apideck.com/file-storage/upload-sessions/ID \
      --header 'Authorization: Bearer API_KEY' \
      --header 'x-apideck-app-id: APP_ID' \
      --header 'x-apideck-consumer-id: CONSUMER_ID' \
      --header 'x-apideck-metadata: {"name": "video.mp4","parent_folder_id": "root"}' \
      --header 'x-apideck-service-id: google-drive'
```

### 3. Upload parts of file

Use the `part_size` from the previous step to split the file to be uploaded into parts. You can use the split command to do this:

```sh meta=terminal
split -b part_size video.mp4 part
```

Upload the parts in sequence to the [`PUT /file-storage/upload-sessions/ID`](/apis/file-storage/reference#operation/uploadSessionsUpload) endpoint. Send a valid `part_number` with each request (`part_number` starts at 0). Always provide valid `Content-Type` and `Content-Length` headers. If you are unsure of the content type of the uploaded file, use `application/octet-stream`.

```sh meta=terminal
curl  --request PUT \
      --url https://upload.apideck.com/file-storage/upload-sessions/ID?part_number=0 \
      --header 'Authorization: Bearer API_KEY' \
      --header 'Content-Type: video/mp4' \
      --header 'x-apideck-app-id: APP_ID' \
      --header 'x-apideck-consumer-id: CONSUMER_ID' \
      --header 'x-apideck-service-id: google-drive' \
      --header 'Digest: sha=pnDkxRveXuqyu1hxTYrO5megpvc=' \
      --data-binary @partaa
```

> **Note 🚨**
>
> For the Box connector it is required to send a `Digest` header with a hash of the part’s contents.
> More info in the Box API documentation{' '}
> <Link href="https://developer.box.com/reference/put-files-upload-sessions-id/#param-digest">
> here
> </Link>

### 4. Finish upload session

The final step is to finish the session using the [`POST /file-storage/upload-sessions/ID/finish`](/apis/file-storage/reference#operation/uploadSessionsFinish) endpoint. The result of the API call is a complete [File](/apis/file-storage/reference#operation/filesOne) resource.

```sh meta=terminal
curl  --request POST \
      --url https://upload.apideck.com/file-storage/upload-sessions/ID/finish \
      --header 'Authorization: Bearer API_KEY' \
      --header 'Content-Type: application/json' \
      --header 'x-apideck-app-id: APP_ID' \
      --header 'x-apideck-consumer-id: CONSUMER_ID' \
      --header 'x-apideck-service-id: google-drive' \
      --header 'Digest: sha=pnDkxRveXuqyu1hxTYrO5megpvc=' \
      --data '{}'
```

> **Note 🚨**
>
> For the Box connector it is required to send a Digest header with a hash of the whole file’s
> contents. More info in the Box API documentation{' '}
> <Link href="https://developer.box.com/reference/post-files-upload-sessions-id-commit/#param-digest">
> here
> </Link>

## FAQ and troubleshooting

### What happens if I upload image.png but image.png already exists?

When there is a conflict (file with same name), these are behaviors that can happen:

- **Autorename**: renames file (image (2).png)
- **Replace**: silently replaces the current file
- **Error**: returns an error
- **New**: creates a new file with the same name (files are not unique by name)

Support for each behavior varies per connector:

| Connector    | Autorename | Replace | Error | New |
| ------------ | ---------- | ------- | ----- | --- |
| Dropbox      | ✅         | ☑️      | ☑️    | 🔴  |
| Box          | 🔴         | 🔴      | ✅    | 🔴  |
| OneDrive     | ☑️         | ✅      | ☑️    | 🔴  |
| SharePoint   | ☑️         | ✅      | ☑️    | 🔴  |
| Google Drive | 🔴         | 🔴      | 🔴    | ✅  |

- ✅ Supported by the integration and supported by Unify
- ☑️ Supported by the integration but not supported by Unify at this moment
- 🔴 Not supported by the integration

Apideck can't unify conflict-handling behavior across connectors because no single behavior is supported by all of them. `Error` comes closest: only Google Drive doesn't support it.
