Uploads
Artwork goes straight from your server to our storage through a signed URL, so large files never pass through the API itself.
Create an upload URL
PUT the bytes to uploadUrl with exactly the returned contentType, then reference url as a line's imageUrl. Content-Length is baked into the signature, so the declared size is the enforced size.
Request body
| Field | Type | Notes |
|---|---|---|
| filename required | string | Used for the extension and for the studio's own records. It does not become the stored object name. |
| contentType required | string | One of |
| sizeBytes required | integer | Exact byte length. Baked into the signature as Content-Length, so the declared size is the enforced size - a mismatch is rejected by storage, not by us. |
Response
| Field | Type | Notes |
|---|---|---|
| uploadUrl required | string | PUT the bytes here. Short-lived - request it when you are ready to send. |
| contentType required | string | Send exactly this as the PUT's |
| url required | string | What you put on a quote line as |
| key required | string | The underlying storage key. Useful in a support conversation; you do not need it to order. |
Status codes
201 | A signed upload target. |
400 | The request was rejected. Branch on |
401 | Missing, malformed, revoked, expired, or belonging to a disabled account - deliberately indistinguishable. |
403 | The key authenticated but lacks the required scope. |
429 | Rate limited. Honour |
POST https://lucentimaging.com.au/api/print/v1/uploads
{
"filename": "artwork.tif",
"contentType": "image/tiff",
"sizeBytes": 84213760
}Accepted types are JPEG, PNG, WebP, TIFF and HEIC, up to 500 MB - a full-size giclée scan genuinely reaches that.
Sending the file
The signature covers both the content type and the content length you declared, so the PUT has to match what you asked for. If it does not, storage rejects it before we ever see it.
# 1. Ask for a signed target. The declared size is the ENFORCED size.
SIZE=$(wc -c < artwork.tif)
UPLOAD=$(curl -s "$LUCENT_API/uploads" \
-H "Authorization: Bearer $LUCENT_KEY" \
-H "Content-Type: application/json" \
-d "{\"filename\":\"artwork.tif\",\"contentType\":\"image/tiff\",\"sizeBytes\":$SIZE}")
# 2. PUT the bytes with EXACTLY the content type you declared.
curl -s -X PUT "$(echo "$UPLOAD" | jq -r .data.uploadUrl)" \
-H "Content-Type: image/tiff" \
--upload-file artwork.tif
# 3. Keep this - it is the line's imageUrl.
IMAGE_URL=$(echo "$UPLOAD" | jq -r .data.url)Artwork ownership
A quote line may only reference artwork uploaded by the account making the request. Anything else is refused with ARTWORK_NOT_ALLOWED at pricing time - before an order exists, not after.
This holds even for a URL that is otherwise valid. Object keys are unguessable, but unguessability is not an authorisation model: a key is not a secret, and a URL that leaks into a log or a support ticket must not become a way to print someone else's work.
The practical consequence is that you cannot pass through a URL you got from anywhere other than your own POST /uploads call - including one from a previous key on a different account. Upload it again.
Products that must print something are marked requiresArtwork in the catalogue. A line for one of those with no imageUrl is refused with ARTWORK_REQUIRED rather than quietly reaching the studio as a job with nothing to print.