--- title: Uploading files | Lightfield description: Upload a file to Lightfield using the file upload lifecycle. --- This guide shows how to upload a file to Lightfield: create an upload session, upload the raw bytes to storage, and complete the file in Lightfield. ## Before you begin You will need: - A valid [API key](/using-the-api/api-keys/index.md) - The `files:create` scope to create, complete, or cancel uploads - The `files:read` scope if you want to verify the uploaded file or fetch a signed download URL All Lightfield API calls in this flow use these headers: ``` Authorization: Bearer YOUR_API_KEY Lightfield-Version: 2026-03-01 Content-Type: application/json ``` The raw byte upload does not go to the Lightfield API. It goes to the presigned `uploadUrl` returned by `POST /v1/files`, and that request should use the returned `uploadHeaders` instead of your Lightfield auth headers. ## Step 1: Create an upload session Create a pending file upload by calling `POST /v1/files`. The `purpose` field determines what the uploaded file will be used for. Each purpose has its own MIME type and size limits, and routes the file to the appropriate storage location. | Purpose | Use for | | --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `meeting_transcript` | A transcript file to attach to a meeting. See [Uploading meeting transcripts](/using-the-api/uploading-meeting-transcripts/index.md). | | `knowledge_user` | A file staged in the authenticated user’s personal uploads area, accessible to the chat agent. See [Uploading knowledge files](/using-the-api/uploading-knowledge-files/index.md). | | `knowledge_workspace` | A file staged in the organization’s shared uploads area, accessible to the chat agent. Requires the caller to be an org admin. See [Uploading knowledge files](/using-the-api/uploading-knowledge-files/index.md). | The example below uses `purpose: "meeting_transcript"` because the meeting transcript flow in the next guide builds directly on this upload lifecycle. Terminal window ``` curl https://api.lightfield.app/v1/files \ -X POST \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Lightfield-Version: 2026-03-01" \ -H "Content-Type: application/json" \ -d '{ "purpose": "meeting_transcript", "filename": "manual-upload-check.txt", "mimeType": "text/plain", "sizeBytes": 29 }' ``` A successful response looks like: ``` { "id": "fil_abc123", "filename": "manual-upload-check.txt", "status": "PENDING", "uploadUrl": "https://...", "uploadHeaders": { "content-type": "text/plain" }, "expiresAt": "2026-04-08T12:00:00.000Z" } ``` Save the returned `id`, `filename`, `uploadUrl`, and `uploadHeaders`. You will use them in the next two steps. For the `knowledge_user` and `knowledge_workspace` purposes, the server stamps the filename with an epoch-millisecond timestamp to keep each upload unique (for example, `playbook.md` becomes `playbook_1713045600000.md`). The `filename` returned in the response reflects the stored name. The `uploadHeaders` will also include `if-none-match: *`, which the presigned PUT requires — do not strip it. See [Collision handling](#collision-handling) below. ## Step 2: Upload the raw bytes Upload the file contents directly to the returned `uploadUrl`. Terminal window ``` printf 'hello from Lightfield upload\n' > /tmp/manual-upload-check.txt curl -i -X PUT "$UPLOAD_URL" \ -H "content-type: text/plain" \ --data-binary @/tmp/manual-upload-check.txt ``` Use the exact headers returned in `uploadHeaders`. The example only shows `content-type`, but your upload session may include additional required headers as well — for knowledge uploads, `uploadHeaders` will also contain `if-none-match: *`, and you must pass it on the PUT. Depending on the storage backend, the upload typically returns `200`, `201`, or `204`. ### Collision handling Knowledge uploads never overwrite an existing object. Two safeguards enforce this: - **Timestamped filenames.** `POST /v1/files` stamps each filename with an epoch-millisecond suffix before the extension (`playbook.md` → `playbook_1713045600000.md`, `archive.tar.gz` → `archive.tar_1713045600000.gz`). The response’s `filename` field returns the stored name. - **Conditional PUT.** The presigned URL for knowledge uploads is signed with `IfNoneMatch: *`, and the required `if-none-match: *` header is included in `uploadHeaders`. If an object already exists at the target key, S3 rejects the PUT with `412 Precondition Failed`. The signed-headers policy means clients cannot strip the header without invalidating the signature. There is no `overwrite` field on the create request. To replace an uploaded file, upload it again — the new timestamped name will land alongside the previous one. ## Step 3: Complete the upload After the raw bytes have been uploaded successfully, finalize the file in Lightfield by calling `POST /v1/files/{id}/complete`. Terminal window ``` curl https://api.lightfield.app/v1/files/$FILE_ID/complete \ -X POST \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Lightfield-Version: 2026-03-01" \ -H "Content-Type: application/json" \ -d '{}' ``` If you computed an MD5 checksum while uploading, you can include it here: ``` { "md5": "5d41402abc4b2a76b9719d911017c592" } ``` After this call, the file status should transition from `PENDING` to `COMPLETED`. ## Optional verification You can verify the upload in two common ways. ### Read the file metadata Terminal window ``` curl https://api.lightfield.app/v1/files/$FILE_ID \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Lightfield-Version: 2026-03-01" ``` This is useful for checking file status and stored metadata. ### Get a temporary download URL Terminal window ``` curl https://api.lightfield.app/v1/files/$FILE_ID/url \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Lightfield-Version: 2026-03-01" ``` This returns a signed URL you can use to download the uploaded file. ## Cancel a pending upload If you created an upload session but do not want to complete it, you can cancel it while the file is still `PENDING`. Terminal window ``` curl https://api.lightfield.app/v1/files/$FILE_ID/cancel \ -X POST \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Lightfield-Version: 2026-03-01" ``` Only pending uploads can be cancelled. ## End-to-end flow The minimal file upload lifecycle is: 1. `POST /v1/files` 2. `PUT ` 3. `POST /v1/files/{id}/complete` Common follow-up calls: 1. `GET /v1/files/{id}` 2. `GET /v1/files/{id}/url` ## Next steps - **[Uploading meeting transcripts](/using-the-api/uploading-meeting-transcripts/index.md)** — Upload a transcript file and attach it to a meeting. - **[Uploading knowledge files](/using-the-api/uploading-knowledge-files/index.md)** — Stage files for the chat agent to promote into your knowledge base. - **[API Reference](/api/index.md)** — Full endpoint reference for files, meetings, and other resources. - **[Errors](/using-the-api/errors/index.md)** — Learn how to handle 4xx and 5xx responses.