Skip to content
Lucent ImagingLucent Imaging icon
API reference

Quotes

Send a basket, get a price that holds for 24 hours. The quote is what you will be charged - order submission never re-prices.

Price a basket

POST
/quotes
price:read
60/min

Omit shippingMethodId to discover the methods this basket can use: the rejection carries every option with its price. Which methods apply depends on the basket, so there is no static list to publish.

Request body

FieldTypeNotes
items
required
array of object

One entry per distinct configuration. Quantity lives on the line, so five copies of one print is one item with quantity: 5.

1-200 items
items[].productSlug
required
string

A slug from the catalogue's products array.

1-100 characters
items[].quantity
required
integer
1-990
items[].variantSelections
required
object (map)

The product's configuration, as a string-to-string map. Dimensions go in as strings. See the table below for which keys each product expects.

items[].imageUrlstring | null

The url returned by POST /uploads. Must be artwork this account uploaded, or the line is refused with ARTWORK_NOT_ALLOWED.

max 2000 characters, URL
items[].externalReferencestring | null

Yours - a SKU or line id. Echoed back on the quote, the order and every webhook. Never priced, never interpreted.

max 128 characters
shippingMethodIdstring | null

Omit deliberately to discover the methods this basket can use - see Shipping discovery below.

max 100 characters
shippingAddress
required
object
shippingAddress.name
required
string
1-200 characters
shippingAddress.companystring | null
max 200 characters
shippingAddress.line1
required
string
1-200 characters
shippingAddress.line2string | null
max 200 characters
shippingAddress.city
required
string
1-100 characters
shippingAddress.state
required
string
1-100 characters
shippingAddress.postcode
required
string
1-20 characters
shippingAddress.country
required
string

ISO-2, e.g. AU. A country NAME is the single most common integration bug here: it silently breaks the carrier quote, so it is rejected at the edge.

exactly 2 characters
shippingAddress.phonestring | null
max 50 characters
shippingAddress.email
required
string (email)

Your customer's address. Required - it travels to the carrier as the parcel's recipient.

max 320 characters
rushTierenum

Leave unset for standard turnaround. Faster tiers carry a fee, returned as rushFeeCents.

"standard" | "express" | "critical"
whiteLabelboolean

Defaults to true: no Lucent branding in the parcel. Set false only if you want the studio's own packaging.

Response

FieldTypeNotes
quoteId
required
string

Pass this to POST /orders. Single-use.

currency
required
string
environment
required
string
pricingVersion
required
integer

Which revision of the pricing engine produced this quote. Handy in a support conversation; nothing to branch on.

-9007199254740991-9007199254740991
expiresAt
required
string

24 hours out. After this the quote can still be read, but not ordered.

consumedAt
required
string | null

Null until the quote becomes an order. A quote stays consumed even if that order is later cancelled.

lines
required
array of object

Positional against the items you sent - this is the one array whose order is meaningful.

lines[].externalReference
required
string | null
lines[].productSlug
required
string
lines[].productName
required
string
lines[].quantity
required
integer
-9007199254740991-9007199254740991
lines[].unitPriceCents
required
integer
-9007199254740991-9007199254740991
lines[].amountCents
required
integer
-9007199254740991-9007199254740991
subtotalCents
required
integer
-9007199254740991-9007199254740991
shippingCents
required
integer
-9007199254740991-9007199254740991
shippingMethod
required
string | null
discountCents
required
integer
-9007199254740991-9007199254740991
discountReason
required
string | null

A stable slug. Branch on this if you branch at all.

discountLabel
required
string | null

The human label for the same discount. Show this.

rushFeeCents
required
integer
-9007199254740991-9007199254740991
promisedReadyDate
required
string | null

A studio-local calendar date where a rush tier guarantees one, else null.

totalCents
required
integer

What the account is charged if this quote becomes an order.

-9007199254740991-9007199254740991
gstCents
required
integer

The tax component already contained within totalCents, for your bookkeeping. Not an addition to it.

-9007199254740991-9007199254740991

Status codes

201

The priced basket.

400

The request was rejected. Branch on error.code.

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 Retry-After.

POST https://lucentimaging.com.au/api/print/v1/quotes

{
  "items": [
    {
      "productSlug": "fine-art-printing",
      "quantity": 5,
      "variantSelections": {
        "artworkWidthMm": "420",
        "artworkHeightMm": "594",
        "mediaId": "0f8c…"
      },
      "imageUrl": "https://lucentimaging.com.au/api/images/order-images/print-api/…",
      "externalReference": "SKU-1187"
    }
  ],
  "shippingMethodId": "m1",
  "shippingAddress": {
    "name": "Jordan Avery",
    "line1": "1 Example Street",
    "city": "Wagga Wagga",
    "state": "NSW",
    "postcode": "2650",
    "country": "AU",
    "email": "[email protected]"
  },
  "rushTier": "standard",
  "whiteLabel": true
}

Variant selections

variantSelections is a string-to-string map because the keys differ per product. Every value is a string, including dimensions: "420", not 420.

Unknown keys are ignored rather than rejected, so a misspelled key does not error - it just does not take effect.

What you can order

ProductAvailable
fine-art-printing
Yes
everyday-art-prints
Yes
print-and-mat
Yes
custom-framing
Yes
canvas-printing
Yes
multi-window-matboard
No

Multi-window matboards are ordered through the storefront - the aperture layout cannot be sent through this API

gift-card
No

Gift cards are not sold through the partner API

paper-samples
No

Paper samples are ordered through the storefront

pro-batch-upload
No

Submit batch work as individual fine-art-printing lines

photo-restoration
No

Photo restoration is quoted manually - contact the studio

fine-art-printing

KeyNotes

artworkWidthMm

required

Printed width in whole millimetres, as a string.

artworkHeightMm

required

Printed height in whole millimetres, as a string.

mediaId

required

An id from the catalogue's media array. Canvas and Everyday Art stocks are separate pools and are refused here.

borderMm

White border around the image.

borderMode

"inside" takes the border out of the artwork dimensions; anything else adds paper around them.

sleeve

A sizeCode from the catalogue's sleeves array. Adds a clear sleeve and backing board.

everyday-art-prints

KeyNotes

artworkWidthMm

required

Printed width in whole millimetres, as a string.

artworkHeightMm

required

Printed height in whole millimetres, as a string.

mediaId

required

An id from the catalogue's everydayArtMedia array - its own exclusive pool.

sleeve

A sizeCode from the catalogue's sleeves array.

print-and-mat

KeyNotes

artworkWidthMm

required

Printed width in whole millimetres, as a string.

artworkHeightMm

required

Printed height in whole millimetres, as a string.

mediaId

required

An id from the catalogue's media array.

matColorId

required

An id from the catalogue's matboards array. Note the spelling - it is not matboardId.

qualityTierId

Constrains which materials may be combined. From qualityTiers.

matSizeMm

Uniform mat border on all four sides. Use this or the per-side keys, not both.

matTopMm

Per-side mat border. Send all four when the borders differ - a weighted bottom is the common case.

matRightMm

Per-side mat border.

matBottomMm

Per-side mat border.

matLeftMm

Per-side mat border.

textWindowWidthMm

Adds a second window below the artwork for a caption. Send with textWindowHeightMm.

textWindowHeightMm

Height of the caption window.

custom-framing

KeyNotes

artworkWidthMm

required

Printed width in whole millimetres, as a string.

artworkHeightMm

required

Printed height in whole millimetres, as a string.

mouldingId

required

An id from the catalogue's mouldings array.

glazingId

required

An id from the catalogue's glazing array.

mediaId

required

The stock the artwork is printed on, from media.

matColorId

Adds a window mount. From matboards; omit for a frame with no mat.

backingId

An id from the catalogue's backing array.

qualityTierId

Constrains which materials may be combined. From qualityTiers.

matSizeMm

Uniform mat border on all four sides. Use this or the per-side keys, not both.

matTopMm

Per-side mat border. Send all four when the borders differ - a weighted bottom is the common case.

matRightMm

Per-side mat border.

matBottomMm

Per-side mat border.

matLeftMm

Per-side mat border.

textWindowWidthMm

Adds a second window below the artwork for a caption. Send with textWindowHeightMm.

textWindowHeightMm

Height of the caption window.

canvas-printing

KeyNotes

artworkWidthMm

required

Printed width in whole millimetres, as a string.

artworkHeightMm

required

Printed height in whole millimetres, as a string.

mediaId

required

An id from the catalogue's canvasMedia array - canvas stocks only.

format

required

"print-only" ships a rolled canvas; anything else is stretched over a frame.

depth

Stretcher bar depth. Required unless format is "print-only". Only "30mm" accepts a float frame.

finish

Surface finish applied after printing.

mouldingId

Adds a float frame. Must be an id from canvasMouldings, and needs a 30mm depth.

Discovering delivery methods

Which methods a basket can use depends on its size, its contents and its destination, so there is no static list and none in the catalogue. Price the basket without a shippingMethodId: the rejection carries every option that basket could use, with prices.

{
  "data": null,
  "error": {
    "code": "SHIPPING_UNAVAILABLE",
    "message": "The selected delivery method isn't available for your cart.",
    "details": {
      "shippingOptions": [
        {
          "id": "m1",
          "name": "Parcel Post",
          "description": null,
          "estimatedDelivery": "3-7 business days",
          "isCollection": false,
          "priceCents": 2200,
          "requiresQuote": false
        }
      ]
    }
  }
}

Retrieve a quote

GET
/quotes/{id}
price:read
120/min

An expired quote is still readable - you need to see what you were quoted and that it lapsed. Only POST /orders refuses to act on one.

Parameters

ParameterInTypeNotes
id
required
pathstring

Response

FieldTypeNotes
quoteId
required
string

Pass this to POST /orders. Single-use.

currency
required
string
environment
required
string
pricingVersion
required
integer

Which revision of the pricing engine produced this quote. Handy in a support conversation; nothing to branch on.

-9007199254740991-9007199254740991
expiresAt
required
string

24 hours out. After this the quote can still be read, but not ordered.

consumedAt
required
string | null

Null until the quote becomes an order. A quote stays consumed even if that order is later cancelled.

lines
required
array of object

Positional against the items you sent - this is the one array whose order is meaningful.

lines[].externalReference
required
string | null
lines[].productSlug
required
string
lines[].productName
required
string
lines[].quantity
required
integer
-9007199254740991-9007199254740991
lines[].unitPriceCents
required
integer
-9007199254740991-9007199254740991
lines[].amountCents
required
integer
-9007199254740991-9007199254740991
subtotalCents
required
integer
-9007199254740991-9007199254740991
shippingCents
required
integer
-9007199254740991-9007199254740991
shippingMethod
required
string | null
discountCents
required
integer
-9007199254740991-9007199254740991
discountReason
required
string | null

A stable slug. Branch on this if you branch at all.

discountLabel
required
string | null

The human label for the same discount. Show this.

rushFeeCents
required
integer
-9007199254740991-9007199254740991
promisedReadyDate
required
string | null

A studio-local calendar date where a rush tier guarantees one, else null.

totalCents
required
integer

What the account is charged if this quote becomes an order.

-9007199254740991-9007199254740991
gstCents
required
integer

The tax component already contained within totalCents, for your bookkeeping. Not an addition to it.

-9007199254740991-9007199254740991

Status codes

200

The quote.

400

The request was rejected. Branch on error.code.

401

Missing, malformed, revoked, expired, or belonging to a disabled account - deliberately indistinguishable.

403

The key authenticated but lacks the required scope.

404

No such quote for this account. Another partner's id answers 404, not 403.

429

Rate limited. Honour Retry-After.

An expired quote is still readable - you need to be able to see what you were quoted and that it lapsed. Only POST /orders refuses to act on one.