---
title: "Register files for upload"
method: POST
path: "/collections/{id}/files"
tags: ["Collections"]
---

# Register files for upload

`POST /collections/{id}/files`

## Request

- Base URL: `https://yungle.co/api/v1`
- URL: `https://yungle.co/api/v1/collections/{id}/files`
- Auth: HTTP bearer

Reserves quota under a per-workspace lock and creates any folders implied by `path`. Returns a tus endpoint and one upload token per file.

## Request body

- object
  - `files` object[]: Files you will upload yourself.
    - `name` string, required: Filename as it should appear to the recipient.
    - `size` integer, required: Exact size in bytes. Must match what you upload.
    - `type` string: MIME type. Best effort; never trusted.
    - `path` string: Directory within an uploaded folder, e.g. "Ceremony/Raw". Omit for a loose file.
  - `imports` object[]: Files Yungle fetches from URLs itself, so the bytes never pass through you. Up to 20 per call. Each is created like an upload (quota, size ceilings, API metering) and imported in the background; follow it with `GET /imports/{fileId}`.
    - `url` string, uri, required: A public http(s) URL Yungle fetches the file from — a presigned S3 link, a CDN, a release asset. It must state the file size (Content-Length, or a Range answer). Private network addresses are refused.
    - `name` string: The file name. Defaults to the one the source gives.
    - `path` string: Folder to place it in, e.g. "Exports/Final".
  - `folderId` string, nullable: Target folder. Omit or null for the collection root.

## Response `201`

Upload targets.

- object
  - `tusEndpoint` string, uri, required: Stream each file here with tus.
  - `files` UploadTarget[], required: One per entry in `files`, in the same order.
    - `id` string, required: File id, for removing it later.
    - `name` string, required
    - `size` integer, required: Bytes, as registered. The upload must be exactly this long.
    - `uploadToken` string, required: Send as `x-yungle-upload-token` on every tus request for this file.
    - `uploadTokenExpiresAt` string, date-time, required: When `uploadToken` stops working (two hours). An upload that may run longer renews it before then: `POST /api/uploads/token` with the current token in `x-yungle-upload-token` returns `{ uploadToken, expiresAt }`. An expired token cannot be renewed.
  - `imports` ImportStarted[], required: One per entry in `imports`, in the same order. Empty when none were sent.
    - `fileId` string, required
    - `name` string, required
    - `size` integer, required: Bytes, as the source stated them.
    - `source` string, required: The host it is fetched from. Never the full URL, which may carry a signature.
    - `status` string, required: Currently one of: `queued`. Treat unknown values as unknown, not as an error.

## Other responses

- `400`: The request was malformed or failed validation. Codes: `invalid_request`, `folder_error`.
- `401`: Missing or invalid credential. Codes: `unauthorized`.
- `402`: Needs a paid plan, or prepaid credit for metered usage. Codes: `upgrade_required`, `out_of_credit`.
- `403`: The credential lacks a required scope, or a shared link refused access. Codes: `insufficient_scope`, `password_required`, `wrong_password`, `guests_only`.
- `404`: No such resource in this workspace. Codes: `not_found`.
- `409`: Already exists, an Idempotency-Key conflict, or end-to-end encrypted content the server cannot serve. Codes: `conflict`, `e2ee_unsupported`.
- `413`: Over the storage quota or the transfer size ceiling. Codes: `quota_exceeded`, `transfer_too_large`.
- `429`: Rate limit or email budget exhausted. Wait for `Retry-After`. Codes: `rate_limited`, `email_budget_exhausted`.
- `500`: Our fault. `details.requestId` ties it to our logs. Codes: `internal_error`.

---

[API](https://skmtc.dev/yungle/apis/yungle-api.md) · [All operations](https://skmtc.dev/yungle/apis/yungle-api/llms.txt) · [OpenAPI document](https://skmtc.dev/yungle/apis/yungle-api/revisions/fee7cb2d7419?raw)
