# Uploading Files 

** Upload a file to Karbon and link it to a contact, organization, client group, or work item. **
---

Use `POST /v3/Files` to upload a file and attach it to a client record or work item. Every upload must be linked to exactly one entity at the time of upload — there is no way to attach a file to multiple records in a single request.

## Overview

`/v3/Files` is the only Karbon endpoint that does not accept JSON. Instead, it takes a `multipart/form-data` body: the binary file as one part, plus a single form field naming the target record. On success it returns the created file's identifier and metadata.

Authentication uses the same `Authorization` and `AccessKey` headers as every other endpoint — see the [Authentication guide](/guides/authentication/) if you don't have these set up yet.

```
POST https://api.karbonhq.com/v3/Files
Authorization: Bearer {token}
AccessKey: {key}
Content-Type: multipart/form-data
```

## Supported file types

The endpoint accepts most everyday document and media formats — PDFs, plain text, CSV, Word and Excel files, images, and similar. Executables, scripts, raw HTML and a handful of other unsafe formats are rejected. If you need to upload a file in a blocked format, add it to a `.zip` archive and upload that instead.

## Request body

| Form field | Type | Required | Notes |
|---|---|---|---|
| `file` | binary | yes | The file to upload, attached as a multipart file part. |
| `contact_keys` | string | * | A single Contact key. |
| `organization_keys` | string | * | A single Organization key. |
| `client_group_keys` | string | * | A single Client Group key. |
| `workitem_keys` | string | * | A single Work Item key. |
| `integration_task_key` | string | * | A single Integration Task key. |

`*` Exactly one link field must be supplied. Despite the plural names, each link field accepts only one key.

### Filename and MIME type

You do **not** need to send the filename or MIME type as separate form fields. Karbon reads both from the file part itself:

- **Filename** — the `filename="…"` parameter on the file part's `Content-Disposition` header (set automatically by every standard HTTP client when you attach a file from disk).
- **MIME type** — the file part's own `Content-Type` header. If absent, Karbon infers the MIME type from the filename extension; if that also fails, the file is stored as `application/octet-stream`.

## Example

```bash
curl -X POST https://api.karbonhq.com/v3/Files \
  -H "Authorization: Bearer {token}" \
  -H "AccessKey: {key}" \
  -F "file=@./proposal.pdf;type=application/pdf" \
  -F "workitem_keys=RXq4mD62PXg"
```

This uploads `proposal.pdf` and links it to a single work item.

## Response

`201 Created`

```json
{
  "@odata.context": "https://api.karbonhq.com/v3/$metadata#Files/$entity",
  "Id": "2S3RNjkR66Ln",
  "Name": "proposal.pdf",
  "MimeType": "application/pdf",
  "Size": "334565"
}
```

## Errors

| Status | Meaning | Common cause |
|---|---|---|
| `400 Bad Request` | No file was included | The request was missing a multipart part named `file`, or the part had no `filename=` on its `Content-Disposition` header. |
| `400 Bad Request` | No entity keys were provided | None of the link fields were populated. Exactly one is required. |
| `400 Bad Request` | Cannot link to more than one entity | More than one link field was populated, or a link field contained more than one key. Only one entity may be linked per upload. |
| `401 Unauthorized` | Missing or invalid bearer token | See [Authentication](/guides/authentication). |
| `429 Too Many Requests` | Rate limit exceeded | See [Rate limits](/guides/rate-limits). |
| `500 Internal Server Error` | Unsupported file type, or an unexpected server error | Retry once; if it persists, contact support with the response body. |

## Attaching to multiple records

To link the same file to several records, upload it once per record — each request returns its own `Id`. There is no endpoint for adding a link to an existing file after the fact.

## Downloading files

This guide covers the upload path only. To download a previously uploaded file, see `GET /v3/Files` with a download token issued by `GET /v3/FileList/{EntityType}`.

### Overriding filename or MIME type

If the file on disk has a misleading name or its detected MIME type is wrong, most HTTP clients let you override what's sent on the file part. In curl, append `filename=` and `type=` to the `-F` value:

```bash
curl -F "file=@./report.bin;filename=q4-summary.pdf;type=application/pdf" ...
```

The same controls exist in most HTTP client libraries. In Python `requests`, pass a tuple: `files={"file": ("q4-summary.pdf", open(path, "rb"), "application/pdf")}`. In JavaScript `fetch`, construct a `File` or `Blob` with the desired name and MIME type.
