Skip to content
Using the API

Uploading files

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.

You will need:

  • A valid API key
  • 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

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.

PurposeUse for
meeting_transcriptA transcript file to attach to a meeting. See Uploading meeting transcripts.
knowledge_userA file staged in the authenticated user’s personal uploads area, accessible to the chat agent. See Uploading knowledge files.
knowledge_workspaceA 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.
email_attachmentA file to attach to a sent email. See Emails and attachments.

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.

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.

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.mdplaybook_1713045600000.md, archive.tar.gzarchive.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.

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.

You can verify the upload in two common ways.

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.

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.

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.

The minimal file upload lifecycle is:

  1. POST /v1/files
  2. PUT <uploadUrl>
  3. POST /v1/files/{id}/complete

Common follow-up calls:

  1. GET /v1/files/{id}
  2. GET /v1/files/{id}/url

Returned by any /v1/files endpoint. 400 responses include a code field; 403, 404, and 409 use the standard envelope without one.

StatusCodeMeaning
400upload_verification_failedThe presigned URL was never uploaded to, or the MD5 checksum did not match. Re-upload the bytes and retry /complete.
400upload_url_expiredThe presigned URL has expired. Create a new upload session.
400file_emptyThe uploaded object is 0 bytes. Re-upload a non-empty file before calling /complete. The file stays PENDING so the same session can be reused.
400file_too_largeThe uploaded file exceeds the per-purpose cap (for example, 3 MB for email_attachment) or the global limit.
400invalid_mime_typeThe file’s MIME type is not allowed for the chosen purpose. The error’s param is mimeType.
400purpose_requires_userThe chosen purpose requires a user-scoped API key.
400limit_too_largeGET /v1/files limit exceeds the maximum allowed page size.
403n/aThe chosen purpose requires an organization admin and the current API key is not.
404n/aFile does not exist or is not accessible to the current API key.
409n/aThe file is in a state that does not allow the requested action — for example, calling /complete on a file that is already COMPLETED, calling /cancel on a non-PENDING file, or downloading a file whose upload never completed.

See Errors for the standard error envelope.