Skip to content

Catalog Product — Detail

Returns a single catalog product by ID with all detail-level fields populated, including translations, images, categories, inventories, customer group prices, and type-specific blocks. This is the read endpoint for the admin Catalog → Products edit form.

Endpoint

EndpointMethodAuthentication
/api/admin/catalog/products/{id}GETAdmin Bearer token

{id} must be a positive integer. The route carries a requirements: ['id' => '\d+'] constraint — non-numeric path segments are rejected with 404 before reaching the provider. This prevents the {id} segment from accidentally matching sibling routes under /catalog/products/.

Authentication

Every request requires:

Authorization: Bearer <token>

Obtain the Bearer token via Authentication.

Path Parameter

ParameterTypeRequiredDescription
idintegerYesThe numeric product ID

Response Shape

The response is a single JSON object (not wrapped in a { data } envelope) with the following top-level fields:

Core fields (always present)

FieldTypeDescription
idintegerProduct ID
skustringProduct SKU
namestringLocalised product name
typestringProduct type (simple, configurable, bundle, grouped, downloadable, virtual, booking)
statusinteger1 = enabled, 0 = disabled
pricestringRaw decimal price string (e.g. "99.9900")
formattedPricestringCurrency-formatted price (e.g. "$99.99")
quantityintegerTotal quantity across all inventory sources
baseImageUrlstring|nullURL of the base/primary image
imagesCountintegerTotal number of product images
categoryIdinteger|nullPrimary category ID
categoryNamestring|nullPrimary category display name
channelstringChannel code used for value resolution
localestringLocale code used for value resolution
attributeFamilyIdintegerAttribute family ID
attributeFamilyNamestringAttribute family display name
urlKeystringURL slug (e.g. classic-watch)
visibleIndividuallybooleanWhether the product appears in listings
shortDescriptionstring|nullShort description (may contain HTML)
descriptionstring|nullFull description (may contain HTML)
metaTitlestring|nullSEO meta title
metaDescriptionstring|nullSEO meta description
metaKeywordsstring|nullSEO meta keywords
weightfloat|nullProduct weight
taxCategoryIdinteger|nullTax category ID
manageStockboolean|nullWhether inventory is managed
inStockbooleanWhether the product is currently in stock
featuredbooleanWhether the product is featured
newbooleanWhether the product is marked as new
createdAtstringISO 8601 creation timestamp
updatedAtstringISO 8601 last-updated timestamp

translations[] array

Each element is one per-locale translation row:

FieldTypeDescription
localestringLocale code (e.g. en, fr)
namestring|nullTranslated product name
descriptionstring|nullTranslated full description
shortDescriptionstring|nullTranslated short description
urlKeystring|nullTranslated URL slug
metaTitlestring|nullTranslated SEO meta title
metaDescriptionstring|nullTranslated SEO meta description
metaKeywordsstring|nullTranslated SEO meta keywords

images[] array

Each element is one product image:

FieldTypeDescription
idintegerImage ID
pathstringStorage path relative to the disk root
urlstringFull public URL
sortOrderintegerDisplay order position

categories[] array

Each element is a category the product belongs to:

FieldTypeDescription
idintegerCategory ID
namestringCategory display name
slugstringCategory URL slug

inventories[] array

Each element is one inventory source row:

FieldTypeDescription
sourceIdintegerInventory source ID
sourceCodestringInventory source code (e.g. default)
qtyintegerQuantity at this source

customerGroupPrices[] array

Each element is a customer-group price override. Empty array ([]) when none are configured.

Type-specific blocks

These four fields are null unless the product type matches:

FieldPresent for typeDescription
superAttributesconfigurableArray of configurable attributes (id, code, label, options)
variantsconfigurableArray of variant child products with their attribute values
bundleOptionsbundleArray of bundle option groups with their selectable products
linkedProductsgroupedArray of linked associated products
downloadableLinksdownloadableArray of download link rows (title, type, url/file, price, downloads)
downloadableSamplesdownloadableArray of sample download rows

channels[] array

Every channel in the store, each flagged with whether this product is assigned to it — mirrors the Channels checkbox box on the edit screen (all options shown, the product's ones ticked). The singular channel field above is the channel code the data was resolved for.

FieldTypeDescription
idintegerChannel ID
codestringChannel code
namestringChannel display name
assignedbooleantrue if this product is assigned to the channel

attributes[] array

The product's attribute-family field set — the same fields the admin edit screen renders, in the same order, driven by the product's attribute family. This includes family-specific fields (e.g. color, size, brand, product_number) that aren't top-level columns. Fields with no value are still present, with value: null.

FieldTypeDescription
idintegerAttribute ID
codestringAttribute code (e.g. sku, color, meta_title)
adminNamestringField label as shown in the admin
typestringInput type (text, textarea, price, boolean, select, multiselect, checkbox, date, datetime, image, file)
isRequiredbooleanWhether the field is required
valuePerChannelbooleanWhether the value can differ per channel
valuePerLocalebooleanWhether the value can differ per locale
groupCodestringCode of the field group it belongs to
groupNamestringDisplay name of the field group
valuemixed|nullThe product's resolved value for the requested channel/locale (null when unset). For select it's the chosen option ID; for multiselect/checkbox a comma-separated list of option IDs
optionsarray|nullFor select/multiselect/checkbox: the selectable options (id, adminName, swatchValue, sortOrder). null for other types

Plain arrays — no follow-up calls needed

All nested fields (translations, images, categories, inventories, customerGroupPrices, channels, attributes, and all type-specific blocks) are serialized as plain inline JSON arrays — there are no IRI strings or sub-resource links. The full product structure is returned in a single response.

Reconstructing the edit screen

attributes + channels together give you everything the admin edit form shows: channels renders the channel checkboxes (with the assigned ones ticked) and attributes renders every General / Description / Meta / Settings / Price field for the product's family. The top-level convenience fields (sku, status, urlKey, …) are also present inside attributes — they're the same values surfaced twice.

Listing vs detail

The GET /api/admin/catalog/products listing endpoint returns a slim { data, meta } envelope with ~18 fields per row (suitable for datagrid rendering). This detail endpoint returns all fields, including the heavy blocks that are intentionally omitted from the listing for performance.

Example Request

bash
curl -X GET "https://your-domain.com/api/admin/catalog/products/42" \
  -H "Authorization: Bearer <token>" \
  -H "Accept: application/json"

Example Response (simple product)

json
{
  "id": 42,
  "sku": "SP-001",
  "name": "Classic Watch",
  "type": "simple",
  "status": 1,
  "price": "99.9900",
  "formattedPrice": "$99.99",
  "quantity": 42,
  "baseImageUrl": "http://localhost:8000/storage/product/42/image.webp",
  "imagesCount": 3,
  "categoryId": 5,
  "categoryName": "Accessories",
  "channel": "default",
  "locale": "en",
  "attributeFamilyId": 1,
  "attributeFamilyName": "Default",
  "urlKey": "classic-watch",
  "visibleIndividually": true,
  "shortDescription": "A premium timepiece.",
  "description": "Full HTML description.",
  "metaTitle": null,
  "metaDescription": null,
  "metaKeywords": null,
  "weight": 0.5,
  "taxCategoryId": null,
  "manageStock": true,
  "inStock": true,
  "featured": false,
  "new": true,
  "createdAt": "2026-01-12T08:15:00+00:00",
  "updatedAt": "2026-04-30T14:20:09+00:00",
  "translations": [
    {
      "locale": "en",
      "name": "Classic Watch",
      "description": "Full HTML description.",
      "shortDescription": "A premium timepiece.",
      "urlKey": "classic-watch",
      "metaTitle": null,
      "metaDescription": null,
      "metaKeywords": null
    }
  ],
  "images": [
    {
      "id": 1,
      "path": "product/42/img1.webp",
      "url": "http://localhost/storage/product/42/img1.webp",
      "sortOrder": 0
    }
  ],
  "categories": [
    {
      "id": 5,
      "name": "Accessories",
      "slug": "accessories"
    }
  ],
  "inventories": [
    {
      "sourceId": 1,
      "sourceCode": "default",
      "qty": 42
    }
  ],
  "customerGroupPrices": [],
  "superAttributes": null,
  "variants": null,
  "bundleOptions": null,
  "linkedProducts": null,
  "downloadableLinks": null,
  "downloadableSamples": null,
  "channels": [
    { "id": 1, "code": "default", "name": "Default Channel", "assigned": true },
    { "id": 2, "code": "mobile", "name": "Mobile Channel", "assigned": false }
  ],
  "attributes": [
    {
      "id": 1,
      "code": "sku",
      "adminName": "SKU",
      "type": "text",
      "isRequired": true,
      "valuePerChannel": false,
      "valuePerLocale": false,
      "groupCode": "general",
      "groupName": "General",
      "value": "SP-001",
      "options": null
    },
    {
      "id": 23,
      "code": "color",
      "adminName": "Color",
      "type": "select",
      "isRequired": false,
      "valuePerChannel": false,
      "valuePerLocale": false,
      "groupCode": "general",
      "groupName": "General",
      "value": null,
      "options": [
        { "id": 1, "adminName": "Red", "swatchValue": "#ff0000", "sortOrder": 1 }
      ]
    },
    {
      "id": 25,
      "code": "meta_title",
      "adminName": "Meta Title",
      "type": "textarea",
      "isRequired": false,
      "valuePerChannel": true,
      "valuePerLocale": true,
      "groupCode": "meta_description",
      "groupName": "Meta Description",
      "value": null,
      "options": null
    }
  ]
}

Errors

HTTP StatusCause
401 UnauthorizedMissing, expired, or revoked admin Bearer token
401 UnauthorizedMissing or invalid admin Bearer token
404 Not FoundThe specified {id} does not exist in the database

Notes

  • Type-aware payload. The six type-specific blocks (superAttributes, variants, bundleOptions, linkedProducts, downloadableLinks, downloadableSamples) are always present in the response, but are null for non-matching types. A simple, virtual, or booking product always has all six as null. Switch on type to know which block to read.
  • All nested objects are plain inline JSON. There are no IRI strings or sub-resource links in the detail response — the full structure (including type-specific blocks) is embedded in a single HTTP call.
  • null fields are always included. Null-valued fields appear explicitly in the JSON rather than being silently dropped.
  • Route disambiguation via \d+ requirement. The {id} path segment is constrained to digits only. Non-numeric segments are rejected with 404 before reaching the provider — this prevents the segment from accidentally matching sibling routes such as /catalog/products/list.
  • Listing vs detail are the same resource. The GET /api/admin/catalog/products listing returns slim datagrid rows; this detail endpoint returns the full payload including all type-specific blocks.
  • Booking products are accessible via this endpoint. Even though booking products cannot be added to an admin draft cart (blocked at cart add-item time with HTTP 400), their detail record is fully readable here.

Released under the MIT License.