Upload a file by sending its bytes with a purpose, and use the file ID it returns, like a submission's file or an account's profilePicture.
Upload
Send the raw bytes as the request body. purpose and name go in the query:
curl -X POST "https://api.onlucra.com/v1/files?purpose=product_image&name=hero.jpg" \
-H "Authorization: Bearer $LUCRA_API_KEY" \
-H "Lucra-Version: 2026-10-01" \
--data-binary @hero.jpg
With the SDK, pass a Blob or File as the body:
import { readFile } from "node:fs/promises"
const { data: file } = await lucra.files.create({
query: { purpose: "product_image", name: "hero.jpg" },
body: new Blob([await readFile("hero.jpg")]),
})
Upload as the account that will use the file. A creator's video is uploaded acting as the creator (Lucra-Account: crtr_…).
Purposes
purpose | What |
|---|---|
submission | A creator's video for a program |
ad_creative | A brand's own video for its library |
product_image | A product image |
profile_picture | A logo or profile photo |
program_brief | A PDF brief |
program_reference_video | An example video for creators |
agreement | A PDF agreement |
chat_attachment | A file attached to a message |
Large files
For a large video, reserve the upload instead: send size and contentType (and optionally checksum, its SHA-256) with an empty body. The response's upload says where to PUT the bytes. Then finish with PATCH /v1/files/:id and { "status": "processing" }.
Processing
Images are ready at once. Videos stay processing while Lucra checks and prepares them; poll GET /v1/files/:id, or wait for the file.ready webhook. Using a file before it's ready returns 409 file_processing.
Endpoints
/v1/filesUpload a file
const { data } = await lucra.files.create({
query: { purpose: "submission" },
body: file,
})Query parameters
purposeenum· requiredsubmissionproduct_imageprofile_pictureprogram_briefprogram_reference_videoagreementchat_attachmentad_creativeprofile_referenceprofile_reference_thumbnailnamestringsizeintegerReserve instead of streaming: the file's size in bytes. The body stays empty.
contentTypestringWith
size: the file's MIME type.checksumstringWith
size: the file's SHA-256 (lowercase hex). Storage then checks the bytes as they arrive, so finishing a video is quicker.ownerstringWith purpose=profile_picture: the signed-in person's own photo, for PATCH /v1/me.
me
Body
The file's raw bytes.
Response20115 fieldsShow
idfile_…purposeenumsubmissionproduct_imageprofile_pictureprogram_briefprogram_reference_videoagreementchat_attachmentad_creativeprofile_referenceprofile_reference_thumbnailstatusenumawaiting_upload: reserved, waiting for the bytes at
upload.url; processing: being verified or prepared; failed carries why.awaiting_uploadprocessingreadyfaileddeletedfileNamestringnullablecontentTypestringnullablesizenumbernullableurlstringnullableA signed link to the file, valid for about 10 minutes; null until it's ready.
widthnumbernullableheightnumbernullableblurHashstringnullableAn image's blur-hash placeholder, once computed.
errorobjectnullableWhy it failed;
upload_incompletewhen the upload was cut off.Show 2 child fieldsHide child fields
codestringmessagestring
uploadobjectWhere to PUT the bytes, with these headers, before
expiresAt; only on a reservation.Show 3 child fieldsHide child fields
urlstringheadersobjectexpiresAtstring
versionintegerSend it back on PATCH to reject edits based on a stale read.
createdAtstringupdatedAtstring
/v1/filesList files
const { data } = await lucra.files.list({
query: { purpose: "…" },
})Query parameters
purposestringOne or more, comma-separated: submission, product_image, profile_picture, program_brief, program_reference_video, agreement, chat_attachment, ad_creative, profile_reference, profile_reference_thumbnail.
statusstringOne or more, comma-separated: awaiting_upload, processing, ready, failed, deleted.
Plus paging: limit, cursor, createdAfter and createdBefore.
Response200A page of items, 15 fields each, and nextCursorShow
idfile_…purposeenumsubmissionproduct_imageprofile_pictureprogram_briefprogram_reference_videoagreementchat_attachmentad_creativeprofile_referenceprofile_reference_thumbnailstatusenumawaiting_upload: reserved, waiting for the bytes at
upload.url; processing: being verified or prepared; failed carries why.awaiting_uploadprocessingreadyfaileddeletedfileNamestringnullablecontentTypestringnullablesizenumbernullableurlstringnullableA signed link to the file, valid for about 10 minutes; null until it's ready.
widthnumbernullableheightnumbernullableblurHashstringnullableAn image's blur-hash placeholder, once computed.
errorobjectnullableWhy it failed;
upload_incompletewhen the upload was cut off.Show 2 child fieldsHide child fields
codestringmessagestring
uploadobjectWhere to PUT the bytes, with these headers, before
expiresAt; only on a reservation.Show 3 child fieldsHide child fields
urlstringheadersobjectexpiresAtstring
versionintegerSend it back on PATCH to reject edits based on a stale read.
createdAtstringupdatedAtstring
/v1/files/:idGet a file
const { data } = await lucra.files.retrieve({
path: { id: "file_…" },
})Path parameters
idfile_…· required
Response20015 fieldsShow
idfile_…purposeenumsubmissionproduct_imageprofile_pictureprogram_briefprogram_reference_videoagreementchat_attachmentad_creativeprofile_referenceprofile_reference_thumbnailstatusenumawaiting_upload: reserved, waiting for the bytes at
upload.url; processing: being verified or prepared; failed carries why.awaiting_uploadprocessingreadyfaileddeletedfileNamestringnullablecontentTypestringnullablesizenumbernullableurlstringnullableA signed link to the file, valid for about 10 minutes; null until it's ready.
widthnumbernullableheightnumbernullableblurHashstringnullableAn image's blur-hash placeholder, once computed.
errorobjectnullableWhy it failed;
upload_incompletewhen the upload was cut off.Show 2 child fieldsHide child fields
codestringmessagestring
uploadobjectWhere to PUT the bytes, with these headers, before
expiresAt; only on a reservation.Show 3 child fieldsHide child fields
urlstringheadersobjectexpiresAtstring
versionintegerSend it back on PATCH to reject edits based on a stale read.
createdAtstringupdatedAtstring
/v1/files/:idFinish or delete an upload
const { data } = await lucra.files.update({
path: { id: "file_…" },
body: { status: "processing" },
})Path parameters
idfile_…· required
Body
statusenum· requiredprocessing: the bytes are at
upload.url, so verify and store them (images are then ready at once); deleted: cancel the upload, or delete a file nothing uses.processingdeleted
Response20015 fieldsShow
idfile_…purposeenumsubmissionproduct_imageprofile_pictureprogram_briefprogram_reference_videoagreementchat_attachmentad_creativeprofile_referenceprofile_reference_thumbnailstatusenumawaiting_upload: reserved, waiting for the bytes at
upload.url; processing: being verified or prepared; failed carries why.awaiting_uploadprocessingreadyfaileddeletedfileNamestringnullablecontentTypestringnullablesizenumbernullableurlstringnullableA signed link to the file, valid for about 10 minutes; null until it's ready.
widthnumbernullableheightnumbernullableblurHashstringnullableAn image's blur-hash placeholder, once computed.
errorobjectnullableWhy it failed;
upload_incompletewhen the upload was cut off.Show 2 child fieldsHide child fields
codestringmessagestring
uploadobjectWhere to PUT the bytes, with these headers, before
expiresAt; only on a reservation.Show 3 child fieldsHide child fields
urlstringheadersobjectexpiresAtstring
versionintegerSend it back on PATCH to reject edits based on a stale read.
createdAtstringupdatedAtstring