# 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 🚨**
>

## 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)

When mapping these behaviors to the integrations, we end up with this matrix:

| 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

The different integrations have different behaviors for handling conflicts with files. Because of these differences it is not possible to "unify" the way handling of duplicate/conflict files, because there is no behavior that is supported for all connectors.
Error comes closest, only Google Drive doesn't support it.
