Skip to content
Lucent ImagingLucent Imaging icon
Getting started

Quickstart

Place a first order end to end. Run these against a test key: they exercise the real code path and the real pricing, but nothing is printed, invoiced or posted.

1. Set up

You need a key from the studio. Every request carries it as a bearer token, and every response comes back as { data, error } - one of the two is always null, so checking error first is the whole error-handling contract.

export LUCENT_KEY="lpk_test_…"
export LUCENT_API="https://lucentimaging.com.au/api/print/v1"

2. Find what you can order

The catalogue lists the products you may order and the material identifiers a line may reference. It carries no prices - a basket is priced by sending it to us, so that your totals and ours cannot disagree.

curl -s "$LUCENT_API/catalogue" \
  -H "Authorization: Bearer $LUCENT_KEY" \
  | jq '{ products: [.data.products[].slug], media: [.data.media[] | {id, name}] }'

3. Upload the artwork

Ask for a signed URL, then PUT the bytes to it. The signature covers both the size and the content type you declared, so they have to match what you actually send.

# 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)

4. Discover the delivery methods

Which methods a basket can use depends on the basket - its size, its contents and where it is going - so there is no static list to fetch. Price the basket without a shippingMethodId and the rejection carries every option it could use, with prices. That is the intended discovery call, not an error to avoid.

# Omit shippingMethodId on purpose: the rejection carries every method this
# basket can use, with prices. There is no static list to fetch.
curl -s "$LUCENT_API/quotes" \
  -H "Authorization: Bearer $LUCENT_KEY" \
  -H "Content-Type: application/json" \
  -d @basket.json \
  | jq '.error.details.shippingOptions'

5. Price the basket

Now send the full basket. You get a quote that holds its price for 24 hours, and the same pricing engine our own storefront uses - including your account's trade discount and any volume ladder you qualify for.

curl -s "$LUCENT_API/quotes" \
  -H "Authorization: Bearer $LUCENT_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "items": [{
      "productSlug": "fine-art-printing",
      "quantity": 5,
      "variantSelections": {
        "artworkWidthMm": "420",
        "artworkHeightMm": "594",
        "mediaId": "'"$MEDIA_ID"'"
      },
      "imageUrl": "'"$IMAGE_URL"'",
      "externalReference": "SKU-1187"
    }],
    "shippingMethodId": "'"$SHIPPING_METHOD_ID"'",
    "shippingAddress": {
      "name": "Jordan Avery",
      "line1": "1 Example Street",
      "city": "Wagga Wagga",
      "state": "NSW",
      "postcode": "2650",
      "country": "AU",
      "email": "[email protected]"
    },
    "rushTier": "standard"
  }' | jq '.data | {quoteId, totalCents, expiresAt}'

Note that variantSelections is a string-to-string map: dimensions go in as "420", not 420. Which keys a product expects is covered on the quotes reference.

6. Place the order

Submitting the quote creates the job and bills your account. This is the one irreversible call in the API, so an Idempotency-Key header is required: retry with the same value and you get your original order back rather than a second one.

curl -s "$LUCENT_API/orders" \
  -H "Authorization: Bearer $LUCENT_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d "{\"quoteId\":\"$QUOTE_ID\",\"specialInstructions\":\"Ship flat, do not roll\"}" \
  | jq '.data | {orderId, orderNumber, status, totalCents}'

We never re-price at submission. Whatever the quote said is what you pay, or you get an explicit QUOTE_EXPIRED - never a surprise total.

7. Follow the job

Poll the order, or register a webhook and we will tell you when it moves. Polling is where most integrations start and is perfectly fine.

curl -s "$LUCENT_API/orders/$ORDER_ID" \
  -H "Authorization: Bearer $LUCENT_KEY" \
  | jq '.data | {status, shipments}'

Next: webhooks so you can stop polling, and errors for everything that can come back instead.