Docs
Firmly Agentic Commerce
Set theme to dark (⇧+D)

Get a Product

GET https://api.firmly.work/api/v1/domains-products/{merchant-domain}/{product-handle}


GET https://api.firmly.work/api/v1/domains-products/{merchant-domain}/{product-handle}

Gets comprehensive product information including variants, pricing, availability, and images. Use this endpoint to display product detail pages or product cards in your application.

​​ Authentication

  • x-firmly-authorization (string, required) — Device access token from Browser Session

​​ Path Parameters

  • merchant-domain (string, required) — The domain of the merchant website (e.g., staging.luma.gift)

  • product-handle (string, required) — The product handle or identifier. This is typically the URL-friendly version of the product name.

​​ Query Parameters

  • postal_code (string) — Postal code to customize pricing, inventory, or availability based on location

​​ Response

  • base_sku (string, required) — The base (parent) SKU identifier for the product.

  • title (string, required) — The product title.

  • handle (string, required) — The product handle / identifier (URL-friendly).

  • description (string) — Product description. Often full HTML.

  • short_description (string) — Short description. May equal description when the merchant doesn’t provide a separate short form.

  • tags (string[]) — Merchant-supplied tags / labels for the product.

  • product_type (string) — Merchant’s category or type for the product.

  • pdp_url (string) — Full URL to the product detail page on the merchant’s site.

  • images (object[]) — Product-level images.

    Image
    • url (string, required) — Image URL.
    • alt (string) — Alternative text.
    • type (string) — Image size variant. One of preview, small, medium, large, default, video. Defaults to default.
    • aspect_ratio (number) — Width-to-height ratio, when known.
    • width (integer) — Pixel width, when known.
    • height (integer) — Pixel height, when known.
    • position (integer) — Ordering position among the product’s images.
    • srcset (object[]) — Alternate renditions of the same image (each with its own url, alt, type, aspect_ratio, width, height).

  • variant_option_values (object[], required) — The option axes a buyer chooses between (e.g. Size, Color), each with its selectable values.

    Variant option axis
    • display_name (string, required) — Display name of the axis (e.g. “Size”, “Color”).
    • property_accessor (string, required) — Key used to reference this axis programmatically.
    • position (integer, required) — Display order of this axis.
    • option_values (object[], required) — Selectable values for this axis.
    Option value
    • display_name (string, required) — Value label (e.g. “Blue”, “Medium”).
    • value (string, required) — Internal value identifier.
    • available (boolean) — Whether this value is currently selectable.
    • swatch (object) — Swatch presentation for the value.
    Swatch
    • url (string) — Swatch image URL.
    • rgb (string) — Swatch color as an RGB / hex string.
    • images (object[]) — Images specific to this value (same Image shape as above).

  • variants (object[], required) — The concrete purchasable variants.

    Variant
    • handle (string, required) — Variant handle / identifier.
    • display_name (string, required) — Variant display name (e.g. “XS / Orange”).
    • title (string, required) — Variant title.
    • sku (string, required) — Variant SKU.
    • add_to_cart_ref (object, required) — Opaque reference for adding this variant to the cart. Pass the entire object as add_to_cart_ref in the Add Line Item body — do not reconstruct it. It commonly contains variant_id, and may also carry product_id, variant_handles, or properties depending on the merchant.
    • variant_option_list (string[], required) — The option values that define this variant, in the axis order of variant_option_values. Each entry is a value from variant_option_values[].option_values[], matched exactly — so ["M", "Orange"] in the example below. These values are merchant-defined and are frequently opaque identifiers rather than readable labels (a live merchant may return ["5", "7"] for the same variant), so never parse or display them. Use display_name for buyer-facing text, and look the entry up in option_values to find its label.
    • description (string) — Variant-specific description.
    • available (boolean) — Whether this variant is currently purchasable.
    • price (object) — Current price.
    Price
    • currency (string, required) — Currency code (e.g. “USD”).
    • value (number, required) — Price as a decimal.
    • number (integer, required) — Price in the smallest currency unit (e.g. cents).
    • symbol (string) — Currency symbol.
    • msrp (object) — Manufacturer’s suggested retail price. Same shape as price.
    • pdp_url (string) — Direct URL to this variant’s product page.
    • variant_attributes (object[]) — Merchant-defined key/value attributes for the variant. Each entry is { "key": string, "value": string }.
    • highlights (string[]) — Short marketing highlights / bullet points.
    • messages (object[]) — Variant-level messages (promotions, stock warnings, badges, etc.).
    Message
    • type (string) — One of promotion, stock, badge, banner, shipping, urgency, info.
    • text (string, required) — Display text.
    • images (object[]) — Images specific to this variant (same Image shape as above).
    • requires_shipping (boolean) — Whether the variant ships physically.
    • taxable (boolean) — Whether the variant is taxable.
    • weight (number) — Variant weight.
    • weight_unit (string) — Unit for weight.
    • modified_at (string) — ISO 8601 timestamp of the variant’s last update.
    • max_purchase_quantity (integer) — Per-SKU purchase cap the merchant enforces but may not show on its public PDP. When set, respect it before calling Add Line Item — exceeding it can cause a merchant-conditional add-line-item rejection. Absent / null means no override.
    • show_quantity_selector (boolean) — Hint for whether the UI should expose a quantity selector for this variant.
    • option1 … option10 (string) — Positional option-value accessors — flat fields (option1, option2, … up to option10) holding each axis value, mirroring variant_option_list. Only as many as the product has axes are populated.

  • properties (object) — Structured product properties.

    Properties
    • vendor (string) — Vendor / brand name.
    • product_family (string) — Product family / grouping.

  • product_attributes (object) — Free-form merchant-specific attributes (e.g. dimensions, material). Schema varies by merchant — treat as an opaque key/value map.

  • reviews (object) — Aggregated review data.

    Reviews
    • total_reviews (integer, required) — Total number of reviews.
    • average_rating (number, required) — Average rating (typically 0–5).
    • review_list_url (string) — URL to the merchant’s full review listing.
    • review_list (object[]) — Individual reviews. Each entry may include reviewer_name, review_date (ISO 8601), rating, review_title, review_text, verified_purchase, helpful_votes.
    • rating_list (object[]) — Per-star distribution. Each entry is { "rating": integer, "count": integer, "percentage": number }.

  • ui_hints (object) — Rendering hints for the storefront UI.

    UI Hints
    • no_variants (boolean) — The product has no meaningful variants — render as a single item.
    • no_options (boolean) — The product has no selectable options — hide option pickers.

  • domain (string) — Merchant domain this product belongs to.

  • domain_name (string) — Merchant display name.

​​ Request Example


curl --request GET \
--url 'https://api.firmly.work/api/v1/domains-products/staging.luma.gift/radiant-tee?postal_code=10001' \
--header 'x-firmly-authorization: YOUR_AUTH_TOKEN'

​​ Response Example


{
"base_sku": "WS12",
"title": "Radiant Tee",
"handle": "radiant-tee",
"description": "A comfortable and stylish tee perfect for any occasion",
"product_type": "Tees",
"images": [
{
"url": "https://cdn.staging.luma.gift/products/radiant-tee-main.jpg",
"type": "default"
}
],
"variant_option_values": [
{
"display_name": "Size",
"property_accessor": "size",
"position": 0,
"option_values": [
{
"display_name": "Small",
"value": "S",
"available": true
},
{
"display_name": "Medium",
"value": "M",
"available": true
},
{
"display_name": "Large",
"value": "L",
"available": false
}
]
},
{
"display_name": "Color",
"property_accessor": "color",
"position": 1,
"option_values": [
{
"display_name": "White",
"value": "WHITE",
"available": true,
"swatch": {
"url": "https://cdn.staging.luma.gift/swatches/white.jpg"
}
},
{
"display_name": "Orange",
"value": "Orange",
"available": true,
"swatch": {
"url": "https://cdn.staging.luma.gift/swatches/orange.jpg"
}
}
]
}
],
"variants": [
{
"handle": "radiant-tee-m-orange",
"sku": "WS12-M-Orange",
"title": "Radiant Tee - M Orange",
"display_name": "M / Orange",
"add_to_cart_ref": {
"variant_id": "WS12-M-Orange"
},
"price": {
"currency": "USD",
"value": 22.00,
"number": 2200,
"symbol": "$"
},
"msrp": {
"currency": "USD",
"value": 22.00,
"number": 2200,
"symbol": "$"
},
"available": true,
"variant_option_list": ["M", "Orange"],
"images": [
{
"url": "https://cdn.staging.luma.gift/products/radiant-tee-orange.jpg",
"type": "default"
}
],
"messages": [
{
"type": "promotion",
"text": "20% off - Limited Time Offer"
},
{
"type": "stock",
"text": "Only 3 left in stock"
}
]
},
{
"handle": "radiant-tee-s-white",
"sku": "WS12-S-White",
"title": "Radiant Tee - S White",
"display_name": "S / White",
"add_to_cart_ref": {
"variant_id": "WS12-S-White"
},
"price": {
"currency": "USD",
"value": 22.00,
"number": 2200,
"symbol": "$"
},
"msrp": {
"currency": "USD",
"value": 22.00,
"number": 2200,
"symbol": "$"
},
"available": true,
"variant_option_list": ["S", "WHITE"],
"images": [
{
"url": "https://cdn.staging.luma.gift/products/radiant-tee-white.jpg",
"type": "default"
}
]
}
],
"reviews": {
"total_reviews": 127,
"average_rating": 4.5
}
}

​​ Error Responses

Errors return a JSON body with code, error, and description. Program against the error value — descriptions are human-readable and may change.

400 — MissingAuthHeader

The x-firmly-authorization header is missing or empty (when authenticating as a device).


{ "code": 400, "error": "MissingAuthHeader", "description": "x-firmly-authorization header is missing or invalid." }
400 — InvalidToken

The authorization token is not a valid JWT structure.


{ "code": 400, "error": "InvalidToken", "description": "Jwt token is invalid." }
401 — InvalidJWTToken

The device JWT signature does not verify, or required claims are missing.


{ "code": 401, "error": "InvalidJWTToken", "description": "Jwt token is invalid." }
404 — PartnerNotFound

The appid claim on the device JWT does not map to a known partner / tenant.


{ "code": 404, "error": "PartnerNotFound", "description": "Partner not found." }
404 — DomainNotFound

The {merchant-domain} does not match any merchant configured with Firmly, or the merchant has been disabled.


{ "code": 404, "error": "DomainNotFound", "description": "This domain was not found in firmly servers." }
404 — ProductNotFound

No product matches the supplied {product-handle} for this merchant.


{ "code": 404, "error": "ProductNotFound", "description": "Product not found." }
412 — OperationNotSupported

The merchant’s adapter does not support this operation.


{ "code": 412, "error": "OperationNotSupported", "description": "This operation is not supported for this store." }
501 — NotImplemented

The merchant’s adapter does not implement product-by-handle lookup. Use Get Product from URL where the merchant supports it.


{ "code": 501, "error": "NotImplemented", "description": "Operation not implemented." }