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

# Upload a File (v2)

> Upload one file of up to 25 MB in one call, for scripts

[← Files](https://code.dcycle.io/api-reference/files/overview.md)

Upload one file in one multipart request. From here on it follows the same path as an upload through the app: the
duplicate check, classification and the links to its folder's projects. The answer is `202 Accepted` with the file and
`Location: /v2/files/{id}`, where [its state](https://code.dcycle.io/api-reference/files/get-v2.md) says how it goes.

Over 25 MB, use the [presigned flow](https://code.dcycle.io/api-reference/files/upload.md), which takes files of up to 100 MB without sending
the bytes through the API.

The file's type comes from its extension and its bytes, never from what the request declares. The `Content-Type` of the
file part is ignored, and bytes that are not what the extension says are refused.

> [!WARNING]
>
> **Beta.** Part of the Import API, in beta. The contract may still change before general availability.

```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl -X POST "https://api.dcycle.io/v2/files" \
  -H "x-api-key: ${DCYCLE_API_KEY}" \
  -H "x-organization-id: ${DCYCLE_ORG_ID}" \
  -H "Idempotency-Key: 8a7b6c5d-4e3f-4a21-9b0c-1d2e3f4a5b6c" \
  -F "file=@residuos-enero.xlsx" \
  -F "facility_id=9b2e4c1a-6d3f-4e8b-a7c5-1f0d2e3b4a59"
```

```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}}
import os
import uuid

import requests

with open("residuos-enero.xlsx", "rb") as file:
    response = requests.post(
        "https://api.dcycle.io/v2/files",
        headers={
            "x-api-key": os.environ["DCYCLE_API_KEY"],
            "x-organization-id": os.environ["DCYCLE_ORG_ID"],
            "Idempotency-Key": str(uuid.uuid4()),
        },
        files={"file": file},
        data={"facility_id": "9b2e4c1a-6d3f-4e8b-a7c5-1f0d2e3b4a59"},
    )
print(response.status_code, response.headers["Location"])
```

```json 202 theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "id": "6f1e2d3c-4b5a-4978-8c6d-5e4f3a2b1c0d",
  "name": "residuos-enero",
  "extension": "xlsx",
  "mime_type": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
  "size_kb": 84,
  "status": "processing",
  "step": "classifying",
  "reason": null,
  "actions": [],
  "review_url": null,
  "organization_id": "a8315ef3-dd50-43f8-b7ce-d839e68d51fa",
  "folder_id": null,
  "facility_id": "9b2e4c1a-6d3f-4e8b-a7c5-1f0d2e3b4a59",
  "transport_direction": null,
  "project_id": null,
  "classifications": [],
  "template_id": null,
  "duplicate": null,
  "auto_import": null,
  "created_at": "2026-11-05T17:20:04Z",
  "updated_at": "2026-11-05T17:20:04Z"
}
```

```json 413 theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "type": "https://code.dcycle.io/api-reference/errors#FILE_TOO_LARGE",
  "title": "Request Entity Too Large",
  "status": 413,
  "code": "FILE_TOO_LARGE",
  "detail": "This route takes files of up to 25 MB. Upload a larger one with POST /v1/files/presigned-urls.",
  "request_id": "b2248a31-0f1b-4a7d-83ce-b29bd5977b4c"
}
```

## Request

### Headers

**x-api-key** `string` *required*

Your API key.

**x-organization-id** `string` *required*

The organization the file goes in.

**Idempotency-Key** `string` *required*

A unique value per file, 1 to 255 characters. Retrying with the same key and file answers the same file with
`Idempotent-Replayed: true` and writes nothing. The same key with another file, name or context, or on another route,
is `422 IDEMPOTENCY_KEY_REUSED`. After 24 hours the key is free and a request with it is a new one. See
[The Idempotency-Key](https://code.dcycle.io/api-reference/ingest-jobs/overview.md#the-idempotency-key).

### Body (multipart/form-data)

**file** `file` *required*

The file, up to 25 MB. Its name keeps only the base name, up to 240 characters without the extension, and may not have
control characters: otherwise the answer is `422 REQUEST_VALIDATION_FAILED`.

| Extension | Stored as | Opens in a browser as | What its bytes must be |
| - | - | - | - |
| `pdf` | `application/pdf` | `inline` | A PDF header in its first 1,024 bytes |
| `jpg`, `jpeg`, `png`, `webp`, `tif`, `tiff`, `bmp` | The image's type | `inline` | That format's signature |
| `xlsx`, `docx` | The Office type | `attachment` | An Office archive that does not expand past 200 MB. A password-protected file is refused |
| `xls` | `application/vnd.ms-excel` | `attachment` | An Excel 97-2003 workbook, or an `.xlsx` renamed. HTML or XML exported as `.xls` is refused: convert it to `.xlsx` or `.csv` |
| `doc` | `application/msword` | `attachment` | A Word 97-2003 document, RTF, or a `.docx` renamed |
| `csv`, `txt`, `json`, `xml` | The text type | `attachment` | Text: no NUL bytes (UTF-16 with its BOM is fine) and it does not start as HTML or SVG |

**facility\_id** `string`

A facility of the organization in `x-organization-id`, not of a subsidiary. An import opened from the file takes it.

**transport\_direction** `string`

`upstream` or `downstream`, for shipments. An import opened from the file takes it.

**project\_id** `string`

A project your organization contributes to. The file is linked to it and an import opened from the file goes in it.

**folder\_id** `string`

A folder of the organization in `x-organization-id`. In an evidence folder the file is stored and never processed.

**skip\_processing** `boolean` (default: false)

Store it and nothing else: no classification and no reading. Its `status` is `completed`.

Any other field is rejected.

## Response

The file's state, as in [Get a File (v2)](https://code.dcycle.io/api-reference/files/get-v2.md).

| Header | Meaning |
| - | - |
| `Location` | `/v2/files/{id}` |
| `Idempotent-Replayed` | `true` when the answer comes from an earlier request with the same key and file |

The request also records an [ingest job](https://code.dcycle.io/api-reference/ingest-jobs/list.md) with `entity_type` `files`, born `completed`:
it keeps the `Idempotency-Key`, sends no `ingest_job.finished` and says nothing about how the file goes. For that,
subscribe to [`file.finished`](https://code.dcycle.io/api-reference/webhooks/overview#file-events): uploaded with an API key, the file sends
it each time it stops (imported, waiting for a person or failed).

## Errors

Errors are [problem details](https://code.dcycle.io/api-reference/errors.md). `413 FILE_TOO_LARGE`, `408 UPLOAD_TIMEOUT`, `503 UPLOADS_BUSY`
(retry after `Retry-After`), `400 FILE_EMPTY`, `422 FILE_CONTENT_NOT_ALLOWED`, `422 REFERENCE_NOT_FOUND` (a
`facility_id`, `project_id` or `folder_id` that does not exist or is outside your organization's perimeter, with
`error_params.field`), `403 FILE_LIMIT_REACHED`, `403 API_KEY_SCOPE_DENIED` (a read-only key), `400 BAD_REQUEST` (a body
that is not valid multipart, or with more than one file), `422 REQUEST_VALIDATION_FAILED` (a missing `Idempotency-Key`,
an unknown field, a file name or extension that is not accepted), `422 IDEMPOTENCY_KEY_REUSED`, `409
IDEMPOTENCY_KEY_IN_PROGRESS`, `404 NOT_FOUND` (a retry of a file deleted or in the trash since), and `429` beyond the
[rate limits](https://code.dcycle.io/api-reference/rate-limits.md).

## Limits

| Limit | Value |
| - | - |
| File size | 25 MB |
| Files per request | 1 |
| Time for the body to arrive | 120 seconds |
| Requests | 360 per minute, bursts of 900, per API key or person |
| Files on the free plan | [The plan's limit](https://code.dcycle.io/api-reference/errors.md#FILE_LIMIT_REACHED) |

## Related Endpoints

> [card] [Get a File (v2)](https://code.dcycle.io/api-reference/files/get-v2.md)
>
> The file's state: classification, template and automatic import

> [card] [Open an Import Session](https://code.dcycle.io/api-reference/files/import-sessions-v2.md)
>
> Import a spreadsheet you uploaded

> [card] [Upload Files](https://code.dcycle.io/api-reference/files/upload.md)
>
> Presigned URLs, for files of up to 100 MB


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.