Skip to content
Documentation/OPEN API

Product Library API

API parameters and examples for products, brands, categories and information templates.

Last updated

Quick start

The Product Library API is available at https://open.musedam.cc/api/product. Append an endpoint path, such as /products, to this address.

  1. Obtain a MuseDAM API Key.
  2. Ask a Product Library manager to add the key as an API Key Collaborator in Member settings → Add member and assign the required role.
  3. Call the API from your server with an Authorization: Bearer <key> header.

Authorization uses the key’s team and the collaborator’s current role. Request parameters cannot switch product libraries.

New collaborators have the view_only role by default. Choose a role for the operations you need:

RoleAvailable operations
view_onlyView published products.
usableView, share and download published products.
editableCreate and edit products, access drafts, publish and delete products.
manageManage all resources, including brands, categories, information templates and activity records.

Each role includes the preceding role’s permissions. Replace resource IDs in the examples with actual IDs.

Replace your_api_key with your authorized API Key to query the product list:

bash
curl --request GET 'https://open.musedam.cc/api/product/products?page=1&pageSize=20' \
  --header 'Authorization: Bearer your_api_key'

A successful response has success: true, a product list in data, and paging information in pagination. If the list is empty, check whether the library contains products visible to your role; view_only and usable can only view published products.

Requests and responses

Every request uses Authorization: Bearer <key>. JSON requests use Content-Type: application/json. Endpoint paths below are relative to the service address.

JSON endpoints use the following structure. Successful downloads and exports return files directly.

ResultHow to read it
Successsuccess: true; read the result from data. code and message are null.
Failuresuccess: false and data: null. Handle the HTTP status and code; do not depend on the wording of message.
Validation failureIf the response includes details, use details[].field and details[].message to correct the fields.
Request tracingKeep requestId or the X-Request-Id response header for troubleshooting.

Ordinary lists accept page and pageSize and return a top-level pagination object. totalCount is the total record count; totalPages is the total page count:

ParameterDefaultRange
page1Integer starting at 1.
pageSize20Positive integer; values above 100 are capped at 100.
json
{
  "page": 1,
  "pageSize": 20,
  "totalCount": 42,
  "totalPages": 3
}

Products and assets can become invisible during a request. A page may therefore contain fewer than pageSize records while retaining its original counts. Reading an invisible individual resource returns 404.

Use cursor synchronization to maintain a product copy; ordinary page numbers are for browsing. See each endpoint for filters. Ordinary queries ignore unknown parameters; JSON request bodies reject unknown fields.

Field formats
FieldUsage
Numeric resource IDsPositive integers up to 2147483647.
Template, group, field and version IDsStrings; preserve them as received and do not assume UUID format.
Asset IDsUse id from asset lookup (FilePath ID), not an AssetObject ID or product association ID.
Category iconAssetIdA category icon identifier (CategoryIconAsset).
sizeDecimal string or null, preserving large byte counts.
DatesRFC 3339 strings or null; preserve all six fractional digits in synchronization timestamps.
Product SKUThe API field is named spu.

Legacy or unresolved product media can have id: null. Valid media references are not truncated. See Media links for previews and downloads.

Concurrent updates

Save the strong ETag (including quotes) when reading a brand, category, template, or product. Supply it as If-Match for an update, status change, or deletion. On 412 precondition_failed, read and reconcile again. If omitted, the later write takes precedence. Shares, history, and download records do not use ETag.

Creation and download retries

Resource creation accepts Idempotency-Key, scoped to the team, API key, and operation. Keys allow up to 255 characters and cannot be blank or contain control characters. Retry with the same key and input within 24 hours; changed input returns 409 idempotency_key_reused. Resource creation replays the initial non-media result and rebuilds media using current permissions. Download retries reuse the record but authorize and transfer current files again.

Request limits and errors

Request bodies are limited to 1 MiB. Unknown JSON properties are rejected. Ordinary queries ignore unknown parameters; history and sync queries use stricter validation.

ResultAction
401 / 403Check the key, collaborator role, and resource visibility.
404The resource is missing, deleted, or inaccessible; check its ID.
422 validation_failedCorrect the input. If details is present, read field and message.
409 sku_in_use / name_in_useChange the conflicting SKU or name. Soft-deleted products still reserve their SKU.
409 mutation_response_unavailableThe mutation committed or replayed. Check mutationOutcome; do not blindly create again.
503 write_conflictThe mutation rolled back. Wait according to Retry-After: 1, then retry.
413Reduce the body or download count/size.

Use X-Request-Id or the JSON requestId for troubleshooting. A download error with mutationOutcome means the record committed; retain the original idempotency key when retrying.

Download OpenAPI JSON

Brands

Brand response fields
FieldTypeDescription
idIntegerResource ID. Positive integer ID, maximum 2147483647.
nameStringName.
codeStringCode.
descriptionStringDescription. May be null
websiteStringWebsite URL. May be null
sortOrderIntegerSort order.
isEnabledBooleanWhether enabled.
createdAtStringRFC 3339 timestamp; preserve full precision.
updatedAtStringRFC 3339 timestamp; preserve full precision.

List brands

Endpoint: /brands

Method: GET

Description: Returns public metadata ordered by sortOrder then id. Raw logo URLs are excluded.

Required role: any collaborator role.

Request parameters:

ParameterTypeRequiredDescription
pageIntegerNoPositive page number, default 1.
pageSizeIntegerNoPositive page size, default 20, capped at 100.

Response fields: 200; data: List<Brand>. field details See Responses and pagination for pagination fields.

Request example:

bash
curl --request GET \
  'https://open.musedam.cc/api/product/brands?page=1&pageSize=20' \
  --header 'Authorization: Bearer your_api_key'
Response example
json
{
  "success": true,
  "data": [
    {
      "id": 10,
      "name": "Example brand",
      "code": "example-code",
      "description": null,
      "website": null,
      "sortOrder": 0,
      "isEnabled": true,
      "createdAt": "2026-10-08T10:00:00.000Z",
      "updatedAt": "2026-10-08T10:00:00.000Z"
    }
  ],
  "code": null,
  "message": null,
  "requestId": "11111111-1111-4111-8111-111111111111",
  "pagination": {
    "page": 1,
    "pageSize": 20,
    "totalCount": 1,
    "totalPages": 1
  }
}

Create brand

Endpoint: /brands

Method: POST

Description: Creates a brand; duplicate names are allowed and code is generated by the server. Logo URL restrictions are listed in the parameter table below.

Required role: manage.

Request parameters:

ParameterTypeRequiredDescription
Idempotency-KeyStringNoOptional header. Retry the same key and input within 24 hours; see Idempotent retries.
-BrandCreateYesJSON request body: application/json

BrandCreate fields:

Unknown fields are rejected.

FieldTypeRequiredDescription
nameStringYesTrimmed before validation. Minimum length 1 characters; Maximum length 128 characters
descriptionStringNoMaximum length 512 characters; May be null
logoUrlStringNoNull/empty clears. Only HTTP(S), no credentials, allowlisted MuseDAM CDN or configured storage host; query and fragment removed before storage. No upload; not echoed in reads. Maximum length 512 characters; May be null

Response fields: 201; data: Brand. field details

Response headers: ETag

Request example:

bash
curl --request POST \
  'https://open.musedam.cc/api/product/brands' \
  --header 'Authorization: Bearer your_api_key' \
  --header 'Content-Type: application/json' \
  --data-raw '{
  "name": "Example brand"
}'
Response example
json
{
  "success": true,
  "data": {
    "id": 10,
    "name": "Example brand",
    "code": "example-code",
    "description": null,
    "website": null,
    "sortOrder": 0,
    "isEnabled": true,
    "createdAt": "2026-10-08T10:00:00.000Z",
    "updatedAt": "2026-10-08T10:00:00.000Z"
  },
  "code": null,
  "message": null,
  "requestId": "11111111-1111-4111-8111-111111111111"
}

Read brand

Endpoint: /brands/{id}

Method: GET

Description: Returns the specified brand’s public metadata and current strong ETag. Raw logo URLs are excluded.

Required role: any collaborator role.

Request parameters:

ParameterTypeRequiredDescription
idIntegerYesPath parameter. Positive signed 32-bit resource ID. Positive integer ID, maximum 2147483647.

Response fields: 200; data: Brand. field details

Response headers: ETag

Request example:

bash
curl --request GET \
  'https://open.musedam.cc/api/product/brands/10' \
  --header 'Authorization: Bearer your_api_key'
Response example
json
{
  "success": true,
  "data": {
    "id": 10,
    "name": "Example brand",
    "code": "example-code",
    "description": null,
    "website": null,
    "sortOrder": 0,
    "isEnabled": true,
    "createdAt": "2026-10-08T10:00:00.000Z",
    "updatedAt": "2026-10-08T10:00:00.000Z"
  },
  "code": null,
  "message": null,
  "requestId": "11111111-1111-4111-8111-111111111111"
}

Update brand

Endpoint: /brands/{id}

Method: PATCH

Description: Updates the provided metadata fields; omitted properties remain unchanged. No file upload or generic asset replacement.

Required role: manage.

Request parameters:

ParameterTypeRequiredDescription
idIntegerYesPath parameter. Positive signed 32-bit resource ID. Positive integer ID, maximum 2147483647.
If-MatchStringNoOptional header. Use the ETag from a read; see Concurrent updates.
-BrandPatchYesJSON request body: application/json

BrandPatch fields:

Unknown fields are rejected; Minimum number of fields: 1.

FieldTypeRequiredDescription
nameStringNoTrimmed before validation. Minimum length 1 characters; Maximum length 128 characters
descriptionStringNoMaximum length 512 characters; May be null
logoUrlStringNoNull/empty clears. Only HTTP(S), no credentials, allowlisted MuseDAM CDN or configured storage host; query and fragment removed before storage. No upload; not echoed in reads. Maximum length 512 characters; May be null

Response fields: 200; data: Brand. field details

Response headers: ETag

Request example:

bash
curl --request PATCH \
  'https://open.musedam.cc/api/product/brands/10' \
  --header 'Authorization: Bearer your_api_key' \
  --header 'Content-Type: application/json' \
  --data-raw '{
  "description": "Updated by API"
}'
Response example
json
{
  "success": true,
  "data": {
    "id": 10,
    "name": "Example brand",
    "code": "example-code",
    "description": "Updated by API",
    "website": null,
    "sortOrder": 0,
    "isEnabled": true,
    "createdAt": "2026-10-08T10:00:00.000Z",
    "updatedAt": "2026-10-08T10:05:00.000Z"
  },
  "code": null,
  "message": null,
  "requestId": "11111111-1111-4111-8111-111111111111"
}

Delete brand

Endpoint: /brands/{id}

Method: DELETE

Description: Deletes only a brand with no live categories or products. Returns 409 otherwise; never cascades into products.

Required role: manage.

Request parameters:

ParameterTypeRequiredDescription
idIntegerYesPath parameter. Positive signed 32-bit resource ID. Positive integer ID, maximum 2147483647.
If-MatchStringNoOptional header. Use the ETag from a read; see Concurrent updates.

Response fields: 200; data: DeletedId. Returns data.id, the deleted resource ID.

Response headers: ETag

Request example:

bash
curl --request DELETE \
  'https://open.musedam.cc/api/product/brands/10' \
  --header 'Authorization: Bearer your_api_key'
Response example
json
{
  "success": true,
  "data": {
    "id": 10
  },
  "code": null,
  "message": null,
  "requestId": "11111111-1111-4111-8111-111111111111"
}

Categories

Category trees and deletion

Sibling names must be unique, and parents must belong to the same brand. A brand allows at most 10 levels and 5000 nondeleted nodes. sortOrder is a zero-based sibling position: omission appends new or moved categories and preserves position for same-parent updates. Renaming or moving updates descendant paths.

Creation adds a direct library-default template binding only when that template has no brand bindings or includes this brand. Otherwise creation still succeeds, with ancestor, brand, then library-default resolution. Deletion soft-deletes the entire subtree; any nondeleted product in it causes 409 category_has_products without deleting anything.

Category response fields
FieldTypeDescription
idIntegerResource ID. Positive integer ID, maximum 2147483647.
brandIdIntegerBrand ID. Positive integer ID, maximum 2147483647.
parentIdIntegerParent category ID. Positive integer ID, maximum 2147483647.; May be null
nameStringName.
pathKeyStringCategory path.
descriptionStringDescription. May be null
iconEmojiStringCategory emoji. May be null
iconAssetIdIntegerCategory icon ID. Positive integer ID, maximum 2147483647.; May be null
sortOrderIntegerSort order.
isEnabledBooleanWhether enabled.
createdAtStringRFC 3339 timestamp; preserve full precision.
updatedAtStringRFC 3339 timestamp; preserve full precision.

List categories

Endpoint: /categories

Method: GET

Description: Returns a flat live tree ordered by brandId, parentId, sortOrder and id. Optional brand filter; no implicit subtree expansion.

Required role: any collaborator role.

Request parameters:

ParameterTypeRequiredDescription
pageIntegerNoPositive page number, default 1.
pageSizeIntegerNoPositive page size, default 20, capped at 100.
brandIdIntegerNoQuery parameter. Filter by brand ID within this library. Positive integer ID, maximum 2147483647.

Response fields: 200; data: List<Category>. field details See Responses and pagination for pagination fields.

Request example:

bash
curl --request GET \
  'https://open.musedam.cc/api/product/categories?page=1&pageSize=20' \
  --header 'Authorization: Bearer your_api_key'
Response example
json
{
  "success": true,
  "data": [
    {
      "id": 20,
      "brandId": 10,
      "parentId": null,
      "name": "Catalog",
      "pathKey": "/Catalog",
      "description": null,
      "iconEmoji": null,
      "iconAssetId": null,
      "sortOrder": 0,
      "isEnabled": true,
      "createdAt": "2026-10-08T10:00:00.000Z",
      "updatedAt": "2026-10-08T10:00:00.000Z"
    }
  ],
  "code": null,
  "message": null,
  "requestId": "11111111-1111-4111-8111-111111111111",
  "pagination": {
    "page": 1,
    "pageSize": 20,
    "totalCount": 1,
    "totalPages": 1
  }
}

Create category

Endpoint: /categories

Method: POST

Description: Creates a live node in the specified brand. Parent must be live in the same brand/library. Max depth 10 and max 5000 live nodes per brand. Sibling names must be unique. The library default template is explicitly bound only when it has no brand bindings or includes this brand; otherwise creation succeeds without an explicit binding, preserving ancestor, brand, then library-default template resolution.

Required role: manage.

Request parameters:

ParameterTypeRequiredDescription
Idempotency-KeyStringNoOptional header. Retry the same key and input within 24 hours; see Idempotent retries.
-CategoryCreateYesJSON request body: application/json

CategoryCreate fields:

Unknown fields are rejected.

FieldTypeRequiredDescription
brandIdIntegerYesPositive integer ID, maximum 2147483647.
nameStringYesExact value, not trimmed. Must contain non-whitespace and no slash; unique among live siblings. Minimum length 1 characters; Maximum length 64 characters; Must match ^(?=.*\S)[^/]+$
parentIdIntegerNoPositive integer ID, maximum 2147483647.; May be null
sortOrderIntegerNoSibling insertion index, no greater than destination sibling count. Omitted: append on create/move, preserve for same parent. Minimum: 0; Maximum: 5,000
descriptionStringNoMay be null
iconEmojiStringNoMaximum length 16 characters; May be null
iconAssetIdIntegerNoExisting same-library CategoryIconAsset ID, not FilePath ID. Positive integer ID, maximum 2147483647.; May be null
isEnabledBooleanNo—

Response fields: 201; data: Category. field details

Response headers: ETag

Request example:

bash
curl --request POST \
  'https://open.musedam.cc/api/product/categories' \
  --header 'Authorization: Bearer your_api_key' \
  --header 'Content-Type: application/json' \
  --data-raw '{
  "name": "Catalog",
  "brandId": 10
}'
Response example
json
{
  "success": true,
  "data": {
    "id": 20,
    "brandId": 10,
    "parentId": null,
    "name": "Catalog",
    "pathKey": "/Catalog",
    "description": null,
    "iconEmoji": null,
    "iconAssetId": null,
    "sortOrder": 0,
    "isEnabled": true,
    "createdAt": "2026-10-08T10:00:00.000Z",
    "updatedAt": "2026-10-08T10:00:00.000Z"
  },
  "code": null,
  "message": null,
  "requestId": "11111111-1111-4111-8111-111111111111"
}

Read category

Endpoint: /categories/{id}

Method: GET

Description: Returns the specified nondeleted category and current strong ETag. A missing category or one outside the current library returns 404.

Required role: any collaborator role.

Request parameters:

ParameterTypeRequiredDescription
idIntegerYesPath parameter. Positive signed 32-bit resource ID. Positive integer ID, maximum 2147483647.

Response fields: 200; data: Category. field details

Response headers: ETag

Request example:

bash
curl --request GET \
  'https://open.musedam.cc/api/product/categories/20' \
  --header 'Authorization: Bearer your_api_key'
Response example
json
{
  "success": true,
  "data": {
    "id": 20,
    "brandId": 10,
    "parentId": null,
    "name": "Catalog",
    "pathKey": "/Catalog",
    "description": null,
    "iconEmoji": null,
    "iconAssetId": null,
    "sortOrder": 0,
    "isEnabled": true,
    "createdAt": "2026-10-08T10:00:00.000Z",
    "updatedAt": "2026-10-08T10:00:00.000Z"
  },
  "code": null,
  "message": null,
  "requestId": "11111111-1111-4111-8111-111111111111"
}

Update category

Endpoint: /categories/{id}

Method: PATCH

Description: Updates or moves a node within its existing brand; brandId is immutable. Rejects cycles/depth/node limits and invalid sort index. Updates descendant paths atomically.

Required role: manage.

Request parameters:

ParameterTypeRequiredDescription
idIntegerYesPath parameter. Positive signed 32-bit resource ID. Positive integer ID, maximum 2147483647.
If-MatchStringNoOptional header. Use the ETag from a read; see Concurrent updates.
-CategoryPatchYesJSON request body: application/json

CategoryPatch fields:

Unknown fields are rejected; Minimum number of fields: 1.

FieldTypeRequiredDescription
nameStringNoExact value, not trimmed. Must contain non-whitespace and no slash; unique among live siblings. Minimum length 1 characters; Maximum length 64 characters; Must match ^(?=.*\S)[^/]+$
parentIdIntegerNoPositive integer ID, maximum 2147483647.; May be null
sortOrderIntegerNoSibling insertion index, no greater than destination sibling count. Omitted: append on create/move, preserve for same parent. Minimum: 0; Maximum: 5,000
descriptionStringNoMay be null
iconEmojiStringNoMaximum length 16 characters; May be null
iconAssetIdIntegerNoExisting same-library CategoryIconAsset ID, not FilePath ID. Positive integer ID, maximum 2147483647.; May be null
isEnabledBooleanNo—

Response fields: 200; data: Category. field details

Response headers: ETag

Request example:

bash
curl --request PATCH \
  'https://open.musedam.cc/api/product/categories/20' \
  --header 'Authorization: Bearer your_api_key' \
  --header 'Content-Type: application/json' \
  --data-raw '{
  "description": "Updated by API"
}'
Response example
json
{
  "success": true,
  "data": {
    "id": 20,
    "brandId": 10,
    "parentId": null,
    "name": "Catalog",
    "pathKey": "/Catalog",
    "description": "Updated by API",
    "iconEmoji": null,
    "iconAssetId": null,
    "sortOrder": 0,
    "isEnabled": true,
    "createdAt": "2026-10-08T10:00:00.000Z",
    "updatedAt": "2026-10-08T10:05:00.000Z"
  },
  "code": null,
  "message": null,
  "requestId": "11111111-1111-4111-8111-111111111111"
}

Delete category

Endpoint: /categories/{id}

Method: DELETE

Description: Soft-deletes the entire category subtree only if no live products reference any descendant. Does not delete or demote products.

Required role: manage.

Request parameters:

ParameterTypeRequiredDescription
idIntegerYesPath parameter. Positive signed 32-bit resource ID. Positive integer ID, maximum 2147483647.
If-MatchStringNoOptional header. Use the ETag from a read; see Concurrent updates.

Response fields: 200; data: DeletedId. Returns data.id, the deleted resource ID.

Response headers: ETag

Request example:

bash
curl --request DELETE \
  'https://open.musedam.cc/api/product/categories/20' \
  --header 'Authorization: Bearer your_api_key'
Response example
json
{
  "success": true,
  "data": {
    "id": 20
  },
  "code": null,
  "message": null,
  "requestId": "11111111-1111-4111-8111-111111111111"
}

Information templates

Groups

groups contains 1–200 groups. A template may have at most 200 fields in total.

FieldTypeDescription
idstringRequired for an existing group, 1–128 characters, belonging to this template. Omit for a new group.
namestringRequired, 1–128 characters. Leading and trailing whitespace is trimmed.
fieldsarrayRequired. Fields in the group, described below.

Existing groups keep their stored order; new groups append in the supplied order. Groups do not accept sortOrder or isDefault. The server assigns the default group, which cannot be deleted.

Fields

New fields require key, name, and type. Existing fields require id. When the type is unchanged, omitted properties retain their values, except for sortOrder. Changing type migrates config for the target type; provide new configuration when required.

FieldTypeDescription
idstringRequired for an existing field, 1–128 characters, belonging to this group. Omit for a new field.
keystringRequired for a new field, 1–64 characters. A unique, valid identifier; cannot change for existing fields.
namestringRequired for a new field, 1–128 characters.
typestringRequired for a new field. Supported types appear in the configuration table below.
requiredbooleanOptional. Defaults to false for a new field.
placeholderstring or nullOptional, up to 255 characters. Defaults to null for a new field.
sortOrderintegerOptional, nonnegative, and unique within the group. If omitted, uses the field's array position.
configobjectDepends on the field type; see below. Defaults to {} for a new field, but must still meet that type's configuration requirements.
typeChangeConfirmedbooleanOptional. Set to true for an allowed type change that requires confirmation.

Creation seeds three protected fields: name, SKU, and main image. This leaves room for up to 197 custom fields. Custom fields in the first group follow those three fields: their supplied sortOrder (or array position when omitted) is increased by 3. Replacement definitions must keep these field IDs, types, required flags, and configurations. Fields cannot move between groups.

New fields must be custom fields and cannot use reserved system keys. isSystemField and systemKey cannot create or reassign system identities; preserve their values on update. Unknown group or field properties are rejected.

Field configuration

config depends on the field's type. For example, text fields use {}, while currency and choice fields need additional configuration.

Field typeConfiguration
text, textarea, email, phone, url, color, date, dateRangeUse {}. Other configuration properties are not accepted.
numberOptional unit, a string.
currencyRequired code and symbol (nonempty strings), and decimals (an integer from 0 to 4).
richtextRequired mode, either html or delta.
select, radio, checkboxRequired, nonempty options array. Entries follow the table below; value must be unique.
image, fileOptional maxCount (an integer of at least 1), maxSizeMB (a number greater than 0), and accept (an array of strings).

Each options entry contains:

FieldTypeDescription
valuestringRequired, nonempty. The option's unique value.
labelstringRequired, nonempty. The display label.
sortOrderintegerRequired, nonnegative.
archivedbooleanOptional. Whether the option is archived.

System fields support customPlaceholder, an optional string of up to 255 characters. System brand and category fields do not accept custom options. Protected system fields cannot change their configuration.

Empty and system configurations reject unknown properties. Other configuration and option validators strip unknown properties. Download the OpenAPI JSON for the full machine-readable definition.

Returned template definition

Template details use groups; products use templateDefinition.groups. Both contain groups and fields. Products return the version pinned at creation, unchanged by later template edits or deletion. templateDefinition may be null when no template is assigned.

Each group always returns id, name, fields, and these read-only properties:

FieldTypeDescription
isDefaultbooleanWhether this is the default group.
sortOrderintegerThe group's order.

Each field always returns id, key, name, type, required, placeholder, sortOrder, config, and the following properties. The write-only parameter typeChangeConfirmed is not returned.

FieldTypeDescription
isSystemFieldbooleanWhether this is a system field.
systemKeystring or nullSystem identity: name, sku, brand, category, description, or mainImage. null for custom fields.

placeholder may be null. Media references in configuration return safe preview information without storage paths or credentials. Reading a product's pinned template definition does not require templates:read.

Template response fields
FieldTypeDescription
idStringResource ID. Minimum length 1 characters; Maximum length 128 characters
nameStringName.
descriptionStringDescription. May be null
enabledBooleanWhether the template is enabled.
isSystemDefaultBooleanWhether this is the system default template.
isLibraryDefaultBooleanWhether this is the library default template.
createdAtStringRFC 3339 timestamp; preserve full precision.
updatedAtStringRFC 3339 timestamp; preserve full precision.
boundBrandIdsList<Integer>Bound brand IDs. Each item: Positive integer ID, maximum 2147483647.
boundCategoryIdsList<Integer>Bound category IDs. Each item: Positive integer ID, maximum 2147483647.
groupsList<VersionGroup>Template groups. Nested fields: Template groups and fields

List templates

Endpoint: /product-info-templates

Method: GET

Description: Returns live template metadata, bindings and ordered groups/fields, including preview-only configuration. Ordered by createdAt descending then id.

Required role: any collaborator role.

Request parameters:

ParameterTypeRequiredDescription
pageIntegerNoPositive page number, default 1.
pageSizeIntegerNoPositive page size, default 20, capped at 100.

Response fields: 200; data: List<Template>. field details See Responses and pagination for pagination fields.

Request example:

bash
curl --request GET \
  'https://open.musedam.cc/api/product/product-info-templates?page=1&pageSize=20' \
  --header 'Authorization: Bearer your_api_key'
Response example
json
{
  "success": true,
  "data": [
    {
      "id": "template_example",
      "name": "Example template",
      "description": null,
      "enabled": true,
      "isSystemDefault": false,
      "isLibraryDefault": false,
      "createdAt": "2026-10-08T10:00:00.000Z",
      "updatedAt": "2026-10-08T10:00:00.000Z",
      "boundBrandIds": [],
      "boundCategoryIds": [],
      "groups": [
        {
          "id": "group_details",
          "name": "Details",
          "isDefault": true,
          "sortOrder": 0,
          "fields": [
            {
              "id": "field_name",
              "key": "name",
              "name": "商品名称",
              "type": "text",
              "required": true,
              "placeholder": null,
              "sortOrder": 0,
              "isSystemField": true,
              "systemKey": "name",
              "config": {}
            },
            {
              "id": "field_sku",
              "key": "sku",
              "name": "SKU",
              "type": "text",
              "required": true,
              "placeholder": null,
              "sortOrder": 1,
              "isSystemField": true,
              "systemKey": "sku",
              "config": {}
            },
            {
              "id": "field_main_image",
              "key": "mainImage",
              "name": "商品主图",
              "type": "image",
              "required": true,
              "placeholder": null,
              "sortOrder": 2,
              "isSystemField": true,
              "systemKey": "mainImage",
              "config": {}
            },
            {
              "id": "field_color",
              "key": "color",
              "name": "Color",
              "type": "text",
              "required": false,
              "placeholder": null,
              "sortOrder": 3,
              "isSystemField": false,
              "systemKey": null,
              "config": {}
            }
          ]
        }
      ]
    }
  ],
  "code": null,
  "message": null,
  "requestId": "11111111-1111-4111-8111-111111111111",
  "pagination": {
    "page": 1,
    "pageSize": 20,
    "totalCount": 1,
    "totalPages": 1
  }
}

Create template

Endpoint: /product-info-templates

Method: POST

Description: Creates a template and an immutable version. syncStrategy is optional and only newOnly is supported. Allows at most 200 fields and leaves existing products unchanged.

Required role: manage.

Request parameters:

ParameterTypeRequiredDescription
Idempotency-KeyStringNoOptional header. Retry the same key and input within 24 hours; see Idempotent retries.
-TemplateCreateYesJSON request body: application/json

TemplateCreate fields:

Trim name/description. At most 200 fields across all groups. Bindings must be same-library, replace the provided binding set, and may move a binding from another template. When selected brands are nonempty, selected categories must belong to those brands. Only newOnly is supported; never bulk-repin existing products.

Unknown fields are rejected.

FieldTypeRequiredDescription
nameStringYesMinimum length 1 characters; Maximum length 30 characters
descriptionStringNoMaximum length 50 characters; May be null
enabledBooleanNo—
groupsList<TemplateGroupInput>YesMinimum items 1; Maximum items 200 Nested fields: Template group fields
boundBrandIdsList<Integer>NoEach item: Positive integer ID, maximum 2147483647.; Maximum items 10,000
boundCategoryIdsList<Integer>NoEach item: Positive integer ID, maximum 2147483647.; Maximum items 10,000
isLibraryDefaultBooleanNo—
syncStrategyStringNoAllowed values: newOnly; Default: "newOnly"

Response fields: 201; data: Template. field details

Response headers: ETag

Request example:

bash
curl --request POST \
  'https://open.musedam.cc/api/product/product-info-templates' \
  --header 'Authorization: Bearer your_api_key' \
  --header 'Content-Type: application/json' \
  --data-raw '{
  "name": "Example template",
  "groups": [
    {
      "name": "Details",
      "fields": [
        {
          "key": "color",
          "name": "Color",
          "type": "text",
          "config": {}
        }
      ]
    }
  ]
}'
Response example
json
{
  "success": true,
  "data": {
    "id": "template_example",
    "name": "Example template",
    "description": null,
    "enabled": true,
    "isSystemDefault": false,
    "isLibraryDefault": false,
    "createdAt": "2026-10-08T10:00:00.000Z",
    "updatedAt": "2026-10-08T10:00:00.000Z",
    "boundBrandIds": [],
    "boundCategoryIds": [],
    "groups": [
      {
        "id": "group_details",
        "name": "Details",
        "isDefault": true,
        "sortOrder": 0,
        "fields": [
          {
            "id": "field_name",
            "key": "name",
            "name": "商品名称",
            "type": "text",
            "required": true,
            "placeholder": null,
            "sortOrder": 0,
            "isSystemField": true,
            "systemKey": "name",
            "config": {}
          },
          {
            "id": "field_sku",
            "key": "sku",
            "name": "SKU",
            "type": "text",
            "required": true,
            "placeholder": null,
            "sortOrder": 1,
            "isSystemField": true,
            "systemKey": "sku",
            "config": {}
          },
          {
            "id": "field_main_image",
            "key": "mainImage",
            "name": "商品主图",
            "type": "image",
            "required": true,
            "placeholder": null,
            "sortOrder": 2,
            "isSystemField": true,
            "systemKey": "mainImage",
            "config": {}
          },
          {
            "id": "field_color",
            "key": "color",
            "name": "Color",
            "type": "text",
            "required": false,
            "placeholder": null,
            "sortOrder": 3,
            "isSystemField": false,
            "systemKey": null,
            "config": {}
          }
        ]
      }
    ]
  },
  "code": null,
  "message": null,
  "requestId": "11111111-1111-4111-8111-111111111111"
}

Read template

Endpoint: /product-info-templates/{id}

Method: GET

Description: Returns the specified nondeleted template’s metadata, bindings, and groups/fields, including preview-only configuration. Groups and fields are ordered by sortOrder then id, ascending. The current strong ETag covers live fields, groups, bindings, and the default designation.

Required role: any collaborator role.

Request parameters:

ParameterTypeRequiredDescription
idStringYesPath parameter. Opaque template ID; do not assume UUID syntax. Minimum length 1 characters; Maximum length 128 characters

Response fields: 200; data: Template. field details

Response headers: ETag

Request example:

bash
curl --request GET \
  'https://open.musedam.cc/api/product/product-info-templates/template_example' \
  --header 'Authorization: Bearer your_api_key'
Response example
json
{
  "success": true,
  "data": {
    "id": "template_example",
    "name": "Example template",
    "description": null,
    "enabled": true,
    "isSystemDefault": false,
    "isLibraryDefault": false,
    "createdAt": "2026-10-08T10:00:00.000Z",
    "updatedAt": "2026-10-08T10:00:00.000Z",
    "boundBrandIds": [],
    "boundCategoryIds": [],
    "groups": [
      {
        "id": "group_details",
        "name": "Details",
        "isDefault": true,
        "sortOrder": 0,
        "fields": [
          {
            "id": "field_name",
            "key": "name",
            "name": "商品名称",
            "type": "text",
            "required": true,
            "placeholder": null,
            "sortOrder": 0,
            "isSystemField": true,
            "systemKey": "name",
            "config": {}
          },
          {
            "id": "field_sku",
            "key": "sku",
            "name": "SKU",
            "type": "text",
            "required": true,
            "placeholder": null,
            "sortOrder": 1,
            "isSystemField": true,
            "systemKey": "sku",
            "config": {}
          },
          {
            "id": "field_main_image",
            "key": "mainImage",
            "name": "商品主图",
            "type": "image",
            "required": true,
            "placeholder": null,
            "sortOrder": 2,
            "isSystemField": true,
            "systemKey": "mainImage",
            "config": {}
          },
          {
            "id": "field_color",
            "key": "color",
            "name": "Color",
            "type": "text",
            "required": false,
            "placeholder": null,
            "sortOrder": 3,
            "isSystemField": false,
            "systemKey": null,
            "config": {}
          }
        ]
      }
    ]
  },
  "code": null,
  "message": null,
  "requestId": "11111111-1111-4111-8111-111111111111"
}

Update template

Endpoint: /product-info-templates/{id}

Method: PATCH

Description: Uses the shared browser batch operation. Omitting the groups property preserves the existing definition; supplying groups replaces the groups and fields in full. Stable IDs and protected system/default fields are validated. Creates a new immutable version without migrating existing product pins or values; bindings/default changes affect future template resolution.

Required role: manage.

Request parameters:

ParameterTypeRequiredDescription
idStringYesPath parameter. Opaque template ID; do not assume UUID syntax. Minimum length 1 characters; Maximum length 128 characters
If-MatchStringNoOptional header. Use the ETag from a read; see Concurrent updates.
-TemplatePatchYesJSON request body: application/json

TemplatePatch fields:

Omitting the groups property preserves the existing definition. Supplying groups replaces it in full: existing groups and fields omitted from the array are deleted, subject to protected default/system fields. Retain the IDs of groups and fields you want to keep. Every successful write creates a new immutable version, including metadata-only updates.

Unknown fields are rejected; Minimum number of fields: 1.

FieldTypeRequiredDescription
nameStringNoMinimum length 1 characters; Maximum length 30 characters
descriptionStringNoMaximum length 50 characters; May be null
enabledBooleanNo—
groupsList<TemplateGroupInput>NoMinimum items 1; Maximum items 200 Nested fields: Template group fields
boundBrandIdsList<Integer>NoEach item: Positive integer ID, maximum 2147483647.; Maximum items 10,000
boundCategoryIdsList<Integer>NoEach item: Positive integer ID, maximum 2147483647.; Maximum items 10,000
isLibraryDefaultBooleanNo—
syncStrategyStringNoAllowed values: newOnly; Default: "newOnly"

Response fields: 200; data: Template. field details

Response headers: ETag

Request example:

bash
curl --request PATCH \
  'https://open.musedam.cc/api/product/product-info-templates/template_example' \
  --header 'Authorization: Bearer your_api_key' \
  --header 'Content-Type: application/json' \
  --data-raw '{
  "description": "Updated by API"
}'
Response example
json
{
  "success": true,
  "data": {
    "id": "template_example",
    "name": "Example template",
    "description": "Updated by API",
    "enabled": true,
    "isSystemDefault": false,
    "isLibraryDefault": false,
    "createdAt": "2026-10-08T10:00:00.000Z",
    "updatedAt": "2026-10-08T10:05:00.000Z",
    "boundBrandIds": [],
    "boundCategoryIds": [],
    "groups": [
      {
        "id": "group_details",
        "name": "Details",
        "isDefault": true,
        "sortOrder": 0,
        "fields": [
          {
            "id": "field_name",
            "key": "name",
            "name": "商品名称",
            "type": "text",
            "required": true,
            "placeholder": null,
            "sortOrder": 0,
            "isSystemField": true,
            "systemKey": "name",
            "config": {}
          },
          {
            "id": "field_sku",
            "key": "sku",
            "name": "SKU",
            "type": "text",
            "required": true,
            "placeholder": null,
            "sortOrder": 1,
            "isSystemField": true,
            "systemKey": "sku",
            "config": {}
          },
          {
            "id": "field_main_image",
            "key": "mainImage",
            "name": "商品主图",
            "type": "image",
            "required": true,
            "placeholder": null,
            "sortOrder": 2,
            "isSystemField": true,
            "systemKey": "mainImage",
            "config": {}
          },
          {
            "id": "field_color",
            "key": "color",
            "name": "Color",
            "type": "text",
            "required": false,
            "placeholder": null,
            "sortOrder": 3,
            "isSystemField": false,
            "systemKey": null,
            "config": {}
          }
        ]
      }
    ]
  },
  "code": null,
  "message": null,
  "requestId": "11111111-1111-4111-8111-111111111111"
}

Delete template

Endpoint: /product-info-templates/{id}

Method: DELETE

Description: Soft-deletes the live template while retaining immutable versions used by existing products. Current library default requires an explicit replacement or null; default/system protections still apply. Removes bindings without rewriting existing product pins.

Required role: manage.

Request parameters:

ParameterTypeRequiredDescription
idStringYesPath parameter. Opaque template ID; do not assume UUID syntax. Minimum length 1 characters; Maximum length 128 characters
If-MatchStringNoOptional header. Use the ETag from a read; see Concurrent updates.
-TemplateDeleteNoJSON request body: application/json

TemplateDelete fields:

Optional body. If deleting the current library default, this property is required: a different live same-library template ID, or explicit null to clear the default. Historical versions remain available to pinned products.

Unknown fields are rejected.

FieldTypeRequiredDescription
replacementForLibraryDefaultStringNoMinimum length 1 characters; Maximum length 128 characters; May be null

Response fields: 200; data: DeletedTemplateId. Returns data.id, the deleted resource ID.

Response headers: ETag

Request example:

bash
curl --request DELETE \
  'https://open.musedam.cc/api/product/product-info-templates/template_example' \
  --header 'Authorization: Bearer your_api_key' \
  --header 'Content-Type: application/json' \
  --data-raw '{
  "replacementForLibraryDefault": null
}'
Response example
json
{
  "success": true,
  "data": {
    "id": "template_example"
  },
  "code": null,
  "message": null,
  "requestId": "11111111-1111-4111-8111-111111111111"
}

Products

Pinned templates and custom fields

Products save a template version at creation. Interpret templateFields using templateDefinition; later template edits or deletion do not change that version. Ordinary PATCH cannot change the template. templateFields uses pinned field IDs as keys and shallow-merges on update; null clears an individual value.

An explicit templateId takes priority. Otherwise a supplied categoryId resolves the category, ancestors, brand, then library default. Without a category, only the library default is considered, even when a brand is supplied.

Field typeInput format
number, currencyJSON number
dateRangeOrdered pair of date strings
checkboxArray of option values
image, fileArray of FilePath IDs, or null
richtextString in HTML mode; { "ops": [...] } in delta mode

Drafts may omit required values, but supplied and merged fields must meet type and option rules. Publishing or editing a published product validates the complete product. A write may reference at most 100 media items across all fields, using existing same-library FilePath IDs. Omitted media lists stay unchanged; [] clears them. Without coverAssetId, the first imageAssetIds image becomes the cover.

Synchronization and checkpoints
  1. Start with sync=true, omitting updatedAfter for a full scan. For an incremental cycle, use the previous completed syncedUntil.
  2. Merge by product id and updatedAt, retaining the newer version.
  3. Follow nextCursor with sync=true&cursor=... until null, including when data is empty.
  4. Save the completed syncedUntil unchanged, not the last product timestamp.

Cursors bind the key, role, library, filters, and page size; keep them unchanged while paging. Start a new cycle after authorization or filter changes, and scan fully when the scope expands. Preserve all six fractional digits in checkpoints and sync updatedAt; JavaScript Date loses precision. Each cycle reads current state in [updatedAfter, syncedUntil), with the first request’s server time minus 30 seconds as its upper bound. Longer transactions may still affect completeness; this is not an event stream of every change.

Removing deleted or invisible products

Sync does not return deletion events. Periodically send local product IDs to visibility checking, removing local copies marked not_visible. Visibility does not check business filters such as brand, category, or search. For a filtered replica, periodically reconcile the full filtered result or sync all visible products and filter locally.

Preview and download links

Use previewUrl to display product media. For originals, check downloadUrl or use the download operations below; preview access does not establish download permission.

LinkAvailabilityMaximum service-issued lifetime
Derived previewAppropriate viewing permission3600 seconds
Original-image preview fallbackImages only; may also be returned to view_only300 seconds
downloadUrlResolvable, visible association; usable or higher plus products:read and products:download300 seconds

Every service-issued link is also bounded by the API key's expiry. Fetch product data again after a link expires; do not persist signed URLs as asset identifiers.

Which fields do covers, images, and related assets return?

cover is one media object, or null when there is no cover. images and relatedAssets are arrays of media objects. Custom image/file values may be arrays or null; unset fields may be omitted.

FieldTypeDescription
idInteger or nullID returned by asset lookup (FilePath ID), not an AssetObject ID or product-asset association ID
name, mimeTypeString or nullFile name and MIME type
sizeString or nullDecimal byte count, such as "2048"; never a JSON number
width, height, durationNumber or nullMedia dimensions and duration
previewUrlURL or nullPreview for display; null when unavailable
downloadUrlURL or null; may be omittedOriginal file URL; availability is described above
downloadUrlExpiresAtTimestamp or null; may be omittedDownload URL expiry in RFC 3339 format
How do missing links, legacy media, and external previews behave?
  • Non-image assets only return derived previews. Original PDFs, archives, and documents never serve as preview fallbacks. Library asset lookup and template configuration contain previews only.
  • Missing download permission or an unresolved association omits downloadUrl and downloadUrlExpiresAt. With permission but an unavailable original, those values may be null.
  • Stored external DAM thumbnails or previews are returned only to API keys without expiry, after excluding known original URLs. The provider controls their lifetime; short expiry and provider-signature validation are not guaranteed.
  • For an API key with expiresAt, an external preview whose expiry cannot be verified is null. Signature-like query parameters are not a trusted expiry source.
  • Service-issued URLs keep the limits above. Signing failure never falls back to a raw storage URL.
ProductSummary response fields
FieldTypeDescription
idIntegerResource ID. Positive integer ID, maximum 2147483647.
nameStringName.
spuStringProduct SKU. May be null
descriptionStringDescription. May be null
statusStringCurrent status. Allowed values: draft, published
brandIdIntegerBrand ID. Positive integer ID, maximum 2147483647.; May be null
categoryIdIntegerCategory ID. Positive integer ID, maximum 2147483647.; May be null
ownerUserIdIntegerOwner user ID. Positive integer ID, maximum 2147483647.; May be null
publishedAtStringRFC 3339 timestamp; preserve full precision. null for drafts.
createdAtStringRFC 3339 timestamp; preserve full precision.
updatedAtStringRFC 3339 timestamp; preserve full precision.
draftReasonStringReason for draft status. May be null
templateIdStringTemplate ID. Minimum length 1 characters; Maximum length 128 characters; May be null
templateVersionIdStringPinned template version ID. Minimum length 1 characters; Maximum length 128 characters; May be null
templateFieldsObjectValues keyed by stable template field IDs.
coverObjectProduct cover. Unknown fields are rejected; May be null Nested fields: Cover media fields
imagesList<PublicAsset>Product images. Nested fields: Media fields
relatedAssetsList<PublicAsset>Related assets. Nested fields: Media fields
ProductDetail response fields

Other fields match the product list response fields.

FieldTypeDescription
templateDefinitionNullableTemplateDefinitionPinned template definition. Nested fields: Product template definition

List or synchronize products

Endpoint: /products

Method: GET

Description: Ordinary pagination orders updatedAt/id descending. sync=true uses ascending (updatedAt,id), inclusive lower and exclusive fixed upper bound (PostgreSQL clock minus 30 seconds). A full scan omits updatedAfter; continue every nextCursor, even with an empty data page. Cursor binds key/role/library/filters/pageSize/bounds. Save syncedUntil verbatim only after exhaustion; replay/deduplicate by id and updatedAt. Transactions longer than the 30-second safety window are an accepted v1 limitation. view_only/usable see only published products.

Required role: any collaborator role.

Request parameters:

ParameterTypeRequiredDescription
pageIntegerNoPositive page number, default 1. Not available with sync=true.
pageSizeIntegerNoPositive page size, default 20, capped at 100.
searchStringNoQuery parameter. Trimmed case-insensitive substring: product name/spu, or library asset name.
brandIdIntegerNoQuery parameter. Filter by brand ID within this library. Positive integer ID, maximum 2147483647.
categoryIdIntegerNoQuery parameter. Filter by exact category ID, not its descendants. Positive integer ID, maximum 2147483647.
statusStringNoQuery parameter. Requested product status; does not widen role visibility. Allowed values: draft, published
syncStringNoQuery parameter. true selects bounded keyset synchronization; false is ordinary pagination. Allowed values: true, false; Default: "false"
updatedAfterStringNoQuery parameter. Only with sync=true. Inclusive lower bound; RFC3339 Z/offset, 0..6 fractional digits, valid calendar/offset. Save and reuse syncedUntil as a string. Omit for a full scan. Format: date-time
cursorStringNoQuery parameter. Opaque continuation token, only with sync=true. Binds key, role, library, bounds, filters and page size. On continuation omit other parameters or repeat equivalent values. Duplicate sync parameters are rejected. Minimum length 1 characters; Maximum length 8,192 characters

Response fields: 200; data: List<ProductSummary>. field details See Responses and pagination for pagination fields.

SyncPage

FieldTypeDescription
pageSizeIntegerNumber of items per page. Minimum: 1; Maximum: 100
syncedUntilStringPostgreSQL clock minus 30 seconds, fixed for the cycle. Save this exact string only after nextCursor is null. Format: date-time; Must match ^\d{4}-\d\d-\d\dT\d\d:\d\d:\d\d\.\d{6}Z$
nextCursorStringCursor for the next page; null after the scan is exhausted. Maximum length 8,192 characters; May be null
hasMoreBooleanWhether another synchronization page is available.

Request example:

bash
curl --request GET \
  'https://open.musedam.cc/api/product/products?page=1&pageSize=20' \
  --header 'Authorization: Bearer your_api_key'
Response example
json
{
  "success": true,
  "data": [
    {
      "id": 40,
      "name": "Example product",
      "spu": "EXAMPLE-001",
      "description": null,
      "status": "draft",
      "brandId": 10,
      "categoryId": 20,
      "ownerUserId": null,
      "publishedAt": null,
      "createdAt": "2026-10-08T10:00:00.000Z",
      "updatedAt": "2026-10-08T10:00:00.000Z",
      "draftReason": null,
      "templateId": "template_example",
      "templateVersionId": "version_one",
      "templateFields": {
        "field_color": "Blue"
      },
      "cover": {
        "id": 30,
        "name": "cover.jpg",
        "mimeType": "image/jpeg",
        "size": "9007199254740993",
        "width": 1200,
        "height": 900,
        "duration": null,
        "previewUrl": "https://media.example.test/preview?signature=example"
      },
      "images": [
        {
          "id": 30,
          "name": "cover.jpg",
          "mimeType": "image/jpeg",
          "size": "9007199254740993",
          "width": 1200,
          "height": 900,
          "duration": null,
          "previewUrl": "https://media.example.test/preview?signature=example"
        }
      ],
      "relatedAssets": []
    }
  ],
  "code": null,
  "message": null,
  "requestId": "11111111-1111-4111-8111-111111111111",
  "pagination": {
    "page": 1,
    "pageSize": 20,
    "totalCount": 1,
    "totalPages": 1
  }
}

Response example (Synchronization):

json
{
  "success": true,
  "data": [
    {
      "id": 40,
      "name": "Example product",
      "spu": "EXAMPLE-001",
      "description": null,
      "status": "draft",
      "brandId": 10,
      "categoryId": 20,
      "ownerUserId": null,
      "publishedAt": null,
      "createdAt": "2026-10-08T10:00:00.000Z",
      "updatedAt": "2026-10-08T10:00:00.123456Z",
      "draftReason": null,
      "templateId": "template_example",
      "templateVersionId": "version_one",
      "templateFields": {
        "field_color": "Blue"
      },
      "cover": {
        "id": 30,
        "name": "cover.jpg",
        "mimeType": "image/jpeg",
        "size": "9007199254740993",
        "width": 1200,
        "height": 900,
        "duration": null,
        "previewUrl": "https://media.example.test/preview?signature=example"
      },
      "images": [
        {
          "id": 30,
          "name": "cover.jpg",
          "mimeType": "image/jpeg",
          "size": "9007199254740993",
          "width": 1200,
          "height": 900,
          "duration": null,
          "previewUrl": "https://media.example.test/preview?signature=example"
        }
      ],
      "relatedAssets": []
    }
  ],
  "code": null,
  "message": null,
  "requestId": "11111111-1111-4111-8111-111111111111",
  "pagination": {
    "pageSize": 20,
    "syncedUntil": "2026-10-08T10:01:00.123456Z",
    "nextCursor": null,
    "hasMore": false
  }
}

Response example (Empty continuation page):

json
{
  "success": true,
  "data": [],
  "code": null,
  "message": null,
  "requestId": "11111111-1111-4111-8111-111111111111",
  "pagination": {
    "pageSize": 20,
    "syncedUntil": "2026-10-08T10:01:00.123456Z",
    "nextCursor": "opaque-continuation-token",
    "hasMore": true
  }
}

Create a draft product

Endpoint: /products

Method: POST

Description: Creates draft only. Resolves and pins a template version at creation; template fields validate against that immutable definition. Attach existing same-library FilePath IDs; no upload, remote import or provider write-back. Idempotent replay preserves original scalar result/ETag and reprojects only original media still valid in the same current slot under current grants.

Required role: editable or manage.

Request parameters:

ParameterTypeRequiredDescription
Idempotency-KeyStringNoOptional header. Retry the same key and input within 24 hours; see Idempotent retries.
-ProductCreateYesJSON request body: application/json

ProductCreate fields:

Always creates draft. Explicit templateId must be a live same-library template and takes precedence. When templateId is omitted/null and a non-null categoryId is supplied, resolution follows category or ancestor binding, then that category's brand binding, then library default. When categoryId is omitted or null, only the library default is considered, even if brandId is supplied. If no template resolves, the product has no template. Category must match resulting brand; owner must be a same-team member and defaults to null, not key creator. At most 100 total references across cover/images/related/custom media. Image slots require images; custom media must satisfy pinned count/size/type limits.

Unknown fields are rejected.

FieldTypeRequiredDescription
nameStringNoTrimmed; optional/empty for draft. Maximum length 255 characters
spuStringNoTrimmed; null/blank clears. Unique within the library, including soft-deleted products. Maximum length 64 characters; May be null
descriptionStringNoMay be null
brandIdIntegerNoPositive integer ID, maximum 2147483647.; May be null
categoryIdIntegerNoPositive integer ID, maximum 2147483647.; May be null
ownerUserIdIntegerNoPositive integer ID, maximum 2147483647.; May be null
templateIdStringNoMinimum length 1 characters; Maximum length 128 characters; May be null
templateFieldsObjectNoShallow merge by stable custom field ID; null clears one field. Unknown/system IDs rejected. Types/options validated even for drafts. image/file values are arrays of FilePath IDs or null; not URL/object snapshots. PATCH validates against the pinned version, even if its live template was deleted.
coverAssetIdIntegerNoPositive integer ID, maximum 2147483647.; May be null
imageAssetIdsList<Integer>NoEach item: Positive integer ID, maximum 2147483647.; Maximum items 100
relatedAssetIdsList<Integer>NoEach item: Positive integer ID, maximum 2147483647.; Maximum items 100

Response fields: 201; data: ProductDetail. field details

Response headers: ETag

Request example:

bash
curl --request POST \
  'https://open.musedam.cc/api/product/products' \
  --header 'Authorization: Bearer your_api_key' \
  --header 'Content-Type: application/json' \
  --data-raw '{
  "name": "Example product",
  "spu": "EXAMPLE-001",
  "brandId": 10,
  "categoryId": 20,
  "coverAssetId": 30,
  "imageAssetIds": [
    30
  ]
}'
Response example
json
{
  "success": true,
  "data": {
    "id": 40,
    "name": "Example product",
    "spu": "EXAMPLE-001",
    "description": null,
    "status": "draft",
    "brandId": 10,
    "categoryId": 20,
    "ownerUserId": null,
    "publishedAt": null,
    "createdAt": "2026-10-08T10:00:00.000Z",
    "updatedAt": "2026-10-08T10:00:00.000Z",
    "draftReason": null,
    "templateId": "template_example",
    "templateVersionId": "version_one",
    "templateFields": {
      "field_color": "Blue"
    },
    "cover": {
      "id": 30,
      "name": "cover.jpg",
      "mimeType": "image/jpeg",
      "size": "9007199254740993",
      "width": 1200,
      "height": 900,
      "duration": null,
      "previewUrl": "https://media.example.test/preview?signature=example"
    },
    "images": [
      {
        "id": 30,
        "name": "cover.jpg",
        "mimeType": "image/jpeg",
        "size": "9007199254740993",
        "width": 1200,
        "height": 900,
        "duration": null,
        "previewUrl": "https://media.example.test/preview?signature=example"
      }
    ],
    "relatedAssets": [],
    "templateDefinition": {
      "groups": [
        {
          "id": "group_details",
          "name": "Details",
          "isDefault": true,
          "sortOrder": 0,
          "fields": [
            {
              "id": "field_color",
              "key": "color",
              "name": "Color",
              "type": "text",
              "required": false,
              "placeholder": null,
              "sortOrder": 0,
              "isSystemField": false,
              "systemKey": null,
              "config": {}
            }
          ]
        }
      ]
    }
  },
  "code": null,
  "message": null,
  "requestId": "11111111-1111-4111-8111-111111111111"
}

Read a product and its pinned definition

Endpoint: /products/{id}

Method: GET

Description: Returns an accessible live product with its pinned immutable template definition, even after the live template changes or is deleted. Does not require templates:read. Media preview/download is authorized on every response; download requires products:read plus products:download and role usable or higher. Drafts are invisible to view_only/usable.

Required role: any collaborator role.

Request parameters:

ParameterTypeRequiredDescription
idIntegerYesPath parameter. Positive signed 32-bit resource ID. Positive integer ID, maximum 2147483647.

Response fields: 200; data: ProductDetail. field details

Response headers: ETag

Request example:

bash
curl --request GET \
  'https://open.musedam.cc/api/product/products/40' \
  --header 'Authorization: Bearer your_api_key'
Response example
json
{
  "success": true,
  "data": {
    "id": 40,
    "name": "Example product",
    "spu": "EXAMPLE-001",
    "description": null,
    "status": "draft",
    "brandId": 10,
    "categoryId": 20,
    "ownerUserId": null,
    "publishedAt": null,
    "createdAt": "2026-10-08T10:00:00.000Z",
    "updatedAt": "2026-10-08T10:00:00.000Z",
    "draftReason": null,
    "templateId": "template_example",
    "templateVersionId": "version_one",
    "templateFields": {
      "field_color": "Blue"
    },
    "cover": {
      "id": 30,
      "name": "cover.jpg",
      "mimeType": "image/jpeg",
      "size": "9007199254740993",
      "width": 1200,
      "height": 900,
      "duration": null,
      "previewUrl": "https://media.example.test/preview?signature=example"
    },
    "images": [
      {
        "id": 30,
        "name": "cover.jpg",
        "mimeType": "image/jpeg",
        "size": "9007199254740993",
        "width": 1200,
        "height": 900,
        "duration": null,
        "previewUrl": "https://media.example.test/preview?signature=example"
      }
    ],
    "relatedAssets": [],
    "templateDefinition": {
      "groups": [
        {
          "id": "group_details",
          "name": "Details",
          "isDefault": true,
          "sortOrder": 0,
          "fields": [
            {
              "id": "field_color",
              "key": "color",
              "name": "Color",
              "type": "text",
              "required": false,
              "placeholder": null,
              "sortOrder": 0,
              "isSystemField": false,
              "systemKey": null,
              "config": {}
            }
          ]
        }
      ]
    }
  },
  "code": null,
  "message": null,
  "requestId": "11111111-1111-4111-8111-111111111111"
}

Response example (No template or media):

json
{
  "success": true,
  "data": {
    "id": 40,
    "name": "Example product",
    "spu": "EXAMPLE-001",
    "description": null,
    "status": "draft",
    "brandId": 10,
    "categoryId": 20,
    "ownerUserId": null,
    "publishedAt": null,
    "createdAt": "2026-10-08T10:00:00.000Z",
    "updatedAt": "2026-10-08T10:00:00.000Z",
    "draftReason": null,
    "templateId": null,
    "templateVersionId": null,
    "templateFields": {},
    "cover": null,
    "images": [],
    "relatedAssets": [],
    "templateDefinition": null
  },
  "code": null,
  "message": null,
  "requestId": "11111111-1111-4111-8111-111111111111"
}

Response example (Authorized download):

json
{
  "success": true,
  "data": {
    "id": 40,
    "name": "Example product",
    "spu": "EXAMPLE-001",
    "description": null,
    "status": "draft",
    "brandId": 10,
    "categoryId": 20,
    "ownerUserId": null,
    "publishedAt": null,
    "createdAt": "2026-10-08T10:00:00.000Z",
    "updatedAt": "2026-10-08T10:00:00.000Z",
    "draftReason": null,
    "templateId": "template_example",
    "templateVersionId": "version_one",
    "templateFields": {
      "field_color": "Blue"
    },
    "cover": {
      "id": 30,
      "name": "cover.jpg",
      "mimeType": "image/jpeg",
      "size": "9007199254740993",
      "width": 1200,
      "height": 900,
      "duration": null,
      "previewUrl": "https://media.example.test/preview?signature=example",
      "downloadUrl": "https://media.example.test/original?signature=example",
      "downloadUrlExpiresAt": "2026-10-08T10:05:00.000Z"
    },
    "images": [
      {
        "id": 30,
        "name": "cover.jpg",
        "mimeType": "image/jpeg",
        "size": "9007199254740993",
        "width": 1200,
        "height": 900,
        "duration": null,
        "previewUrl": "https://media.example.test/preview?signature=example"
      }
    ],
    "relatedAssets": [],
    "templateDefinition": {
      "groups": [
        {
          "id": "group_details",
          "name": "Details",
          "isDefault": true,
          "sortOrder": 0,
          "fields": [
            {
              "id": "field_color",
              "key": "color",
              "name": "Color",
              "type": "text",
              "required": false,
              "placeholder": null,
              "sortOrder": 0,
              "isSystemField": false,
              "systemKey": null,
              "config": {}
            }
          ]
        }
      ]
    }
  },
  "code": null,
  "message": null,
  "requestId": "11111111-1111-4111-8111-111111111111"
}

Partially update a product

Endpoint: /products/{id}

Method: PATCH

Description: Merges scalar/custom fields and replaces submitted media lists. Uses the immutable pinned definition, including when the live template is deleted. Published products additionally require products:publish and stay published; full resulting-state validation must pass atomically. Status/template switching is not accepted here.

Required role: editable or manage.

Request parameters:

ParameterTypeRequiredDescription
idIntegerYesPath parameter. Positive signed 32-bit resource ID. Positive integer ID, maximum 2147483647.
If-MatchStringNoOptional header. Use the ETag from a read; see Concurrent updates.
-ProductPatchYesJSON request body: application/json

ProductPatch fields:

Omitted fields remain unchanged; explicit null clears nullable fields and [] clears lists. templateFields merges; media arrays replace their slot. If imageAssetIds is provided without coverAssetId, first image becomes cover. Published products require products:publish and remain published, with full resulting-state validation. templateId, templateVersionId and status are rejected.

Unknown fields are rejected.

FieldTypeRequiredDescription
nameStringNoTrimmed; optional/empty for draft. Maximum length 255 characters
spuStringNoTrimmed; null/blank clears. Unique within the library, including soft-deleted products. Maximum length 64 characters; May be null
descriptionStringNoMay be null
brandIdIntegerNoPositive integer ID, maximum 2147483647.; May be null
categoryIdIntegerNoPositive integer ID, maximum 2147483647.; May be null
ownerUserIdIntegerNoPositive integer ID, maximum 2147483647.; May be null
templateFieldsObjectNoShallow merge by stable custom field ID; null clears one field. Unknown/system IDs rejected. Types/options validated even for drafts. image/file values are arrays of FilePath IDs or null; not URL/object snapshots. PATCH validates against the pinned version, even if its live template was deleted.
coverAssetIdIntegerNoPositive integer ID, maximum 2147483647.; May be null
imageAssetIdsList<Integer>NoEach item: Positive integer ID, maximum 2147483647.; Maximum items 100
relatedAssetIdsList<Integer>NoEach item: Positive integer ID, maximum 2147483647.; Maximum items 100

Response fields: 200; data: ProductDetail. field details

Response headers: ETag

Request example:

bash
curl --request PATCH \
  'https://open.musedam.cc/api/product/products/40' \
  --header 'Authorization: Bearer your_api_key' \
  --header 'Content-Type: application/json' \
  --data-raw '{
  "description": null,
  "templateFields": {
    "field_color": "Blue"
  }
}'
Response example
json
{
  "success": true,
  "data": {
    "id": 40,
    "name": "Example product",
    "spu": "EXAMPLE-001",
    "description": null,
    "status": "draft",
    "brandId": 10,
    "categoryId": 20,
    "ownerUserId": null,
    "publishedAt": null,
    "createdAt": "2026-10-08T10:00:00.000Z",
    "updatedAt": "2026-10-08T10:00:00.000Z",
    "draftReason": null,
    "templateId": "template_example",
    "templateVersionId": "version_one",
    "templateFields": {
      "field_color": "Blue"
    },
    "cover": {
      "id": 30,
      "name": "cover.jpg",
      "mimeType": "image/jpeg",
      "size": "9007199254740993",
      "width": 1200,
      "height": 900,
      "duration": null,
      "previewUrl": "https://media.example.test/preview?signature=example"
    },
    "images": [
      {
        "id": 30,
        "name": "cover.jpg",
        "mimeType": "image/jpeg",
        "size": "9007199254740993",
        "width": 1200,
        "height": 900,
        "duration": null,
        "previewUrl": "https://media.example.test/preview?signature=example"
      }
    ],
    "relatedAssets": [],
    "templateDefinition": {
      "groups": [
        {
          "id": "group_details",
          "name": "Details",
          "isDefault": true,
          "sortOrder": 0,
          "fields": [
            {
              "id": "field_color",
              "key": "color",
              "name": "Color",
              "type": "text",
              "required": false,
              "placeholder": null,
              "sortOrder": 0,
              "isSystemField": false,
              "systemKey": null,
              "config": {}
            }
          ]
        }
      ]
    }
  },
  "code": null,
  "message": null,
  "requestId": "11111111-1111-4111-8111-111111111111"
}

Soft-delete a product

Endpoint: /products/{id}

Method: DELETE

Description: Soft-deletes a visible live product. Draft requires delete; published requires delete and publish. Leaves historical template versions intact. No provider synchronization. A repeated delete returns 404.

Required role: editable or manage.

Request parameters:

ParameterTypeRequiredDescription
idIntegerYesPath parameter. Positive signed 32-bit resource ID. Positive integer ID, maximum 2147483647.
If-MatchStringNoOptional header. Use the ETag from a read; see Concurrent updates.

Response fields: 200; data: DeletedId. Returns data.id, the deleted resource ID.

Response headers: ETag

Request example:

bash
curl --request DELETE \
  'https://open.musedam.cc/api/product/products/40' \
  --header 'Authorization: Bearer your_api_key'
Response example
json
{
  "success": true,
  "data": {
    "id": 40
  },
  "code": null,
  "message": null,
  "requestId": "11111111-1111-4111-8111-111111111111"
}

Publish or return a product to draft

Endpoint: /products/{id}/status

Method: PATCH

Description: Always requires both update and publish grants. Publishing validates all pinned required/type/option/media rules; invalid state returns 422 with field details and makes no changes. Repeated published keeps publishedAt, returning to draft clears it. No approval workflow is started.

Required role: editable or manage.

Request parameters:

ParameterTypeRequiredDescription
idIntegerYesPath parameter. Positive signed 32-bit resource ID. Positive integer ID, maximum 2147483647.
If-MatchStringNoOptional header. Use the ETag from a read; see Concurrent updates.
-ProductStatusYesJSON request body: application/json

ProductStatus fields:

Both products:update and products:publish required for either transition, including same-status requests. Publish validates all required pinned fields and current media; failure is atomic. publishedAt is set on first publication, retained on repeated publication and cleared on draft.

Unknown fields are rejected.

FieldTypeRequiredDescription
statusStringYesAllowed values: draft, published

Response fields: 200; data: ProductDetail. field details

Response headers: ETag

Request example:

bash
curl --request PATCH \
  'https://open.musedam.cc/api/product/products/40/status' \
  --header 'Authorization: Bearer your_api_key' \
  --header 'Content-Type: application/json' \
  --data-raw '{
  "status": "published"
}'
Response example
json
{
  "success": true,
  "data": {
    "id": 40,
    "name": "Example product",
    "spu": "EXAMPLE-001",
    "description": null,
    "status": "published",
    "brandId": 10,
    "categoryId": 20,
    "ownerUserId": null,
    "publishedAt": "2026-10-08T10:00:00.000Z",
    "createdAt": "2026-10-08T10:00:00.000Z",
    "updatedAt": "2026-10-08T10:00:00.000Z",
    "draftReason": null,
    "templateId": "template_example",
    "templateVersionId": "version_one",
    "templateFields": {
      "field_color": "Blue"
    },
    "cover": {
      "id": 30,
      "name": "cover.jpg",
      "mimeType": "image/jpeg",
      "size": "9007199254740993",
      "width": 1200,
      "height": 900,
      "duration": null,
      "previewUrl": "https://media.example.test/preview?signature=example"
    },
    "images": [
      {
        "id": 30,
        "name": "cover.jpg",
        "mimeType": "image/jpeg",
        "size": "9007199254740993",
        "width": 1200,
        "height": 900,
        "duration": null,
        "previewUrl": "https://media.example.test/preview?signature=example"
      }
    ],
    "relatedAssets": [],
    "templateDefinition": {
      "groups": [
        {
          "id": "group_details",
          "name": "Details",
          "isDefault": true,
          "sortOrder": 0,
          "fields": [
            {
              "id": "field_color",
              "key": "color",
              "name": "Color",
              "type": "text",
              "required": false,
              "placeholder": null,
              "sortOrder": 0,
              "isSystemField": false,
              "systemKey": null,
              "config": {}
            }
          ]
        }
      ]
    }
  },
  "code": null,
  "message": null,
  "requestId": "11111111-1111-4111-8111-111111111111"
}

Reconcile known product IDs

Endpoint: /products/visibility

Method: POST

Description: Checks up to 100 known IDs under current key visibility. Keeps order and duplicates, accepts an empty array. not_visible does not distinguish deleted, missing, foreign-library and role-hidden items. Use periodically to remove stale local entries because sync has no deletion tombstones. This read operation has no Idempotency-Key or ETag.

Required role: any collaborator role.

Request parameters:

ParameterTypeRequiredDescription
-VisibilityInputYesJSON request body: application/json

VisibilityInput fields:

0..100 IDs. Output preserves input order and duplicates; [] returns [].

Unknown fields are rejected.

FieldTypeRequiredDescription
idsList<Integer>YesEach item: Positive integer ID, maximum 2147483647.; Maximum items 100

Response fields: 200; data: List<Visibility>.

Visibility

FieldTypeDescription
idIntegerResource ID. Positive integer ID, maximum 2147483647.
visibilityStringVisibility of the submitted product ID. Allowed values: visible, not_visible

Request example:

bash
curl --request POST \
  'https://open.musedam.cc/api/product/products/visibility' \
  --header 'Authorization: Bearer your_api_key' \
  --header 'Content-Type: application/json' \
  --data-raw '{
  "ids": [
    40,
    41
  ]
}'
Response example
json
{
  "success": true,
  "data": [
    {
      "id": 40,
      "visibility": "visible"
    },
    {
      "id": 41,
      "visibility": "not_visible"
    }
  ],
  "code": null,
  "message": null,
  "requestId": "11111111-1111-4111-8111-111111111111"
}

Assets

LibraryAsset response fields
FieldTypeDescription
idIntegerResource ID. Positive integer ID, maximum 2147483647.
nameStringName. May be null
mimeTypeStringMIME type. May be null
sizeStringDecimal byte count string; never a JSON number. Must match ^\d+$; May be null
widthNumberWidth. May be null
heightNumberHeight. May be null
durationNumberDuration. May be null
previewUrlStringPreview URL. Format: uri; May be null

Find existing library assets

Endpoint: /assets

Method: GET

Description: Lists live non-directory FilePath assets in the key library, ordered by id. Requires either products:create or products:update under editable/manage role, not products:read. Returns previews only, never original download fields. Use FilePath id in product writes; uploads are outside v1.

Required role: editable or manage.

Request parameters:

ParameterTypeRequiredDescription
pageIntegerNoPositive page number, default 1.
pageSizeIntegerNoPositive page size, default 20, capped at 100.
searchStringNoQuery parameter. Trimmed case-insensitive substring: product name/spu, or library asset name.
mimeTypeStringNoQuery parameter. Trimmed case-insensitive MIME prefix, for example image/ or application/pdf.

Response fields: 200; data: List<LibraryAsset>. field details See Responses and pagination for pagination fields.

Request example:

bash
curl --request GET \
  'https://open.musedam.cc/api/product/assets?page=1&pageSize=20' \
  --header 'Authorization: Bearer your_api_key'
Response example
json
{
  "success": true,
  "data": [
    {
      "id": 30,
      "name": "cover.jpg",
      "mimeType": "image/jpeg",
      "size": "9007199254740993",
      "width": 1200,
      "height": 900,
      "duration": null,
      "previewUrl": "https://media.example.test/preview?signature=example"
    }
  ],
  "code": null,
  "message": null,
  "requestId": "11111111-1111-4111-8111-111111111111",
  "pagination": {
    "page": 1,
    "pageSize": 20,
    "totalCount": 1,
    "totalPages": 1
  }
}

Read an existing library asset

Endpoint: /assets/{id}

Method: GET

Description: Returns a live same-library FilePath with preview-only media metadata. Same create-or-update gate as listing. Returns an opaque strong ETag based on the FilePath and linked AssetObject identities and maintained update timestamps; temporary signature changes do not change this tag.

Required role: editable or manage.

Request parameters:

ParameterTypeRequiredDescription
idIntegerYesPath parameter. Positive signed 32-bit resource ID. Positive integer ID, maximum 2147483647.

Response fields: 200; data: LibraryAsset. field details

Response headers: ETag

Request example:

bash
curl --request GET \
  'https://open.musedam.cc/api/product/assets/30' \
  --header 'Authorization: Bearer your_api_key'
Response example
json
{
  "success": true,
  "data": {
    "id": 30,
    "name": "cover.jpg",
    "mimeType": "image/jpeg",
    "size": "9007199254740993",
    "width": 1200,
    "height": 900,
    "duration": null,
    "previewUrl": "https://media.example.test/preview?signature=example"
  },
  "code": null,
  "message": null,
  "requestId": "11111111-1111-4111-8111-111111111111"
}

Sharing

Share access

Shares select 1–100 published products but display their current content. Products moved to draft or deleted are hidden. Visitors need no login or API key. Key expiry, deactivation, or collaborator removal does not revoke existing shares; call Revoke share to stop access.

Which fields are in a record's product snapshots?

The products array in share and download records preserves product details at the time of the operation. Later product edits do not update it.

FieldTypeDescription
idPositive integerProduct ID
nameStringProduct name at the time of the operation
spuString or nullSKU at the time of the operation
ProductShare response fields
FieldTypeDescription
idStringResource ID.
urlStringShare URL. Format: uri
productsList<ProductLabelSnapshot>Product snapshots. Nested fields: Product snapshot fields
createdByIdIntegerCreator user ID. May be null
createdByApiKeyIdStringCreator API key ID. May be null
createdByNameStringCreator name.
createdAtStringCreation time. Format: date-time
expiresAtStringExpiry time. May be null; Format: date-time
revokedAtStringRevocation time. May be null; Format: date-time
allowDownloadBooleanWhether downloads are allowed.
hasAccessCodeBooleanWhether the share requires an access code.
statusStringCurrent status. Allowed values: active, expired, revoked
canRevokeBooleanWhether the current caller may revoke the share.

List team share history

Endpoint: /product-shares

Method: GET

Description: Lists records from every browser actor and API key in the current team, newest first by createdAt and id. Optional productId returns complete matching operations; omission selects all team records. Only productId, page and pageSize are accepted; unknown or repeated parameters return 422. The current API key or creator never implicitly narrows the list. Requires manage plus products:history, without products:read.

Required role: manage.

Request parameters:

ParameterTypeRequiredDescription
productIdIntegerNoQuery parameter. Filter matching activity by one product ID. The full operation is returned, including other products and files. Omit for all records in the current team. Positive integer ID, up to 2147483647.
pageIntegerNoPositive page number, default 1.
pageSizeIntegerNoPositive page size, default 20, capped at 100.

Response fields: 200; data: List<ProductShare>. field details See Responses and pagination for pagination fields.

Request example:

bash
curl --request GET \
  'https://open.musedam.cc/api/product/product-shares?page=1&pageSize=20' \
  --header 'Authorization: Bearer your_api_key'
Response example
json
{
  "success": true,
  "data": [
    {
      "id": "share-example",
      "url": "https://your-deployment.example/share/products/example-recipient-token",
      "products": [
        {
          "id": 40,
          "name": "Example product",
          "spu": "SKU-40"
        }
      ],
      "createdById": null,
      "createdByApiKeyId": "integration-key-id",
      "createdByName": "Catalog integration",
      "createdAt": "2026-10-09T10:00:00.000Z",
      "expiresAt": "2026-10-16T10:00:00.000Z",
      "revokedAt": null,
      "allowDownload": false,
      "hasAccessCode": false,
      "status": "active",
      "canRevoke": true
    }
  ],
  "code": null,
  "message": null,
  "requestId": "11111111-1111-4111-8111-111111111111",
  "pagination": {
    "page": 1,
    "pageSize": 20,
    "totalCount": 1,
    "totalPages": 1
  }
}

Create product share

Endpoint: /product-shares

Method: POST

Description: Creates a fixed selection of published products with a 7/30/90-day or unlimited lifetime. accessCode is optional and exactly four digits. allowDownload additionally requires products:download. The recipient needs no login or API key; unpublished/deleted products disappear from the live share. Creator-key changes do not revoke the independent team share. Optional 24-hour idempotency reuses the same share.

Required role: usable, editable or manage.

Request parameters:

ParameterTypeRequiredDescription
Idempotency-KeyStringNoOptional header. Retry the same key and input within 24 hours; see Idempotent retries.
-ProductShareCreateYesJSON request body: application/json

ProductShareCreate fields:

Unknown fields are rejected.

FieldTypeRequiredDescription
productIdsList<Integer>YesMinimum items 1; Maximum items 100. Each ID is a positive integer up to 2147483647.
expiresInDaysIntegerYesMay be null; Allowed values: 7, 30, 90, null
accessCodeStringNoMust match ^[0-9]{4}$
allowDownloadBooleanYes—

Response fields: 201; data: ProductShare. field details

Request example:

bash
curl --request POST \
  'https://open.musedam.cc/api/product/product-shares' \
  --header 'Authorization: Bearer your_api_key' \
  --header 'Content-Type: application/json' \
  --data-raw '{
  "productIds": [
    40
  ],
  "expiresInDays": 7,
  "allowDownload": false
}'
Response example
json
{
  "success": true,
  "data": {
    "id": "share-example",
    "url": "https://your-deployment.example/share/products/example-recipient-token",
    "products": [
      {
        "id": 40,
        "name": "Example product",
        "spu": "SKU-40"
      }
    ],
    "createdById": null,
    "createdByApiKeyId": "integration-key-id",
    "createdByName": "Catalog integration",
    "createdAt": "2026-10-09T10:00:00.000Z",
    "expiresAt": "2026-10-16T10:00:00.000Z",
    "revokedAt": null,
    "allowDownload": false,
    "hasAccessCode": false,
    "status": "active",
    "canRevoke": false
  },
  "code": null,
  "message": null,
  "requestId": "11111111-1111-4111-8111-111111111111"
}

Revoke product share

Endpoint: /product-shares/{id}

Method: DELETE

Description: Revokes any share in the current team. Requires manage and products:share, without products:read. Repeating a revocation returns the original revokedAt. Unknown or foreign-team IDs return 404. No ETag or If-Match is used.

Required role: manage.

Request parameters:

ParameterTypeRequiredDescription
idStringYesPath parameter. Opaque share identifier. Minimum length 1 characters; Maximum length 128 characters

Response fields: 200; data: RevokedProductShare.

RevokedProductShare

FieldTypeDescription
idStringResource ID.
revokedAtStringRevocation time. Format: date-time

Request example:

bash
curl --request DELETE \
  'https://open.musedam.cc/api/product/product-shares/share-example' \
  --header 'Authorization: Bearer your_api_key'
Response example
json
{
  "success": true,
  "data": {
    "id": "share-example",
    "revokedAt": "2026-10-09T11:00:00.000Z"
  },
  "code": null,
  "message": null,
  "requestId": "11111111-1111-4111-8111-111111111111"
}

Downloads

File selection, exports, and records

List product download candidates first. Put the returned key in assetKeys, or URL-encode it as the single-file path parameter assetKey; it is not a FilePath ID. One original returns that file; multiple originals return a ZIP. A request selects 1–100 products, at most 200 files, and no more than 500 MiB.

Exports use kind: "export", with xlsx (default), csv, or json. Do not send assetKeys. includeAssetUrls defaults to true; exported links still require a Bearer header. Create a share for visitors without credentials. locale affects system labels and status only.

X-Download-Record-Id identifies the business record. requested means accepted, not transferred or saved locally. History includes browser and other API key operations across the team. Ordinary reads, previews, and direct access to storage download URLs do not create history records.

Which fields are in a download record's file snapshots?

A download record's files array preserves file details when the operation is first accepted.

FieldTypeDescription
idStringFile snapshot identifier
productIdPositive integerAssociated product ID
nameStringFile name at the time of the operation
ProductDownloadRecord response fields
FieldTypeDescription
idStringResource ID.
kindStringOperation kind. Allowed values: product_export, asset_download
statusStringCurrent status. Allowed values: requested, failed
actorTypeStringActor type. Allowed values: member, external, api_key
actorIdIntegerActor ID. May be null
apiKeyIdStringAPI key ID. May be null
actorNameStringActor name.
shareIdStringShare ID. May be null
productsList<ProductLabelSnapshot>Product snapshots. Nested fields: Product snapshot fields
filesList<FileLabelSnapshot>File snapshots. Nested fields: File snapshot fields
createdAtStringCreation time. Format: date-time
ProductDownloadCandidate response fields
FieldTypeDescription
keyStringDownload selection key.
nameStringName.
productIdIntegerProduct ID. Positive integer ID, up to 2147483647.
productNameStringProduct name.
categoryStringMedia category. Allowed values: main, related, field
mimeTypeStringMIME type. May be null
sizeStringSize in bytes, represented as a decimal string. May be null; Must match ^[0-9]+$
downloadableBooleanWhether this file can currently be downloaded.

List team download history

Endpoint: /product-downloads

Method: GET

Description: Lists records from every browser actor and API key in the current team, newest first by createdAt and id. Optional productId returns complete matching operations; omission selects all team records. Only productId, page and pageSize are accepted; unknown or repeated parameters return 422. The current API key or creator never implicitly narrows the list. Requires manage plus products:history, without products:read.

Required role: manage.

Request parameters:

ParameterTypeRequiredDescription
productIdIntegerNoQuery parameter. Filter matching activity by one product ID. The full operation is returned, including other products and files. Omit for all records in the current team. Positive integer ID, up to 2147483647.
pageIntegerNoPositive page number, default 1.
pageSizeIntegerNoPositive page size, default 20, capped at 100.

Response fields: 200; data: List<ProductDownloadRecord>. field details See Responses and pagination for pagination fields.

Request example:

bash
curl --request GET \
  'https://open.musedam.cc/api/product/product-downloads?page=1&pageSize=20' \
  --header 'Authorization: Bearer your_api_key'
Response example
json
{
  "success": true,
  "data": [
    {
      "id": "download-example",
      "kind": "product_export",
      "status": "requested",
      "actorType": "api_key",
      "actorId": null,
      "apiKeyId": "integration-key-id",
      "actorName": "Catalog integration",
      "shareId": null,
      "products": [
        {
          "id": 40,
          "name": "Example product",
          "spu": "SKU-40"
        }
      ],
      "files": [
        {
          "id": "export:xlsx",
          "productId": 40,
          "name": "products.xlsx"
        }
      ],
      "createdAt": "2026-10-09T10:00:00.000Z"
    }
  ],
  "code": null,
  "message": null,
  "requestId": "11111111-1111-4111-8111-111111111111",
  "pagination": {
    "page": 1,
    "pageSize": 20,
    "totalCount": 1,
    "totalPages": 1
  }
}

Download originals or export products

Endpoint: /product-downloads

Method: POST

Description: Returns one original, a ZIP of selected originals, or an XLSX/CSV/JSON business-table export. Requires current read and download grants under usable or higher; usable sees published products only. Accepted operations are recorded before delivery. Export links default to authenticated single-file GET URLs; each accepted retrieval has its own business record. Optional Idempotency-Key retains one record for 24 hours while each retry reauthorizes and transfers current content. Product reads, previews and legacy storage downloadUrl use are not recorded by this operation.

Required role: usable, editable or manage.

Request parameters:

ParameterTypeRequiredDescription
Idempotency-KeyStringNoOptional header. Retry the same key and input within 24 hours; see Idempotent retries.
-ProductDownloadCreateYesJSON request body: application/json

ProductDownloadCreate fields:

assets selects all originals when assetKeys is omitted, or only the supplied current keys. export rejects assetKeys and produces a localized business table. format, includeAssetUrls and locale apply only to exports; asset downloads accept but ignore them for delivery. Export links default on, require Bearer authentication, and never contain API-key secrets. locale changes system labels/status, not user-authored labels or values.

Unknown fields are rejected.

FieldTypeRequiredDescription
kindStringYesAllowed values: assets, export
productIdsList<Integer>YesMinimum items 1; Maximum items 100. Each ID is a positive integer up to 2147483647.
assetKeysList<String>NoEach item: Minimum length 1 characters; Maximum length 256 characters; Minimum items 1; Maximum items 200
formatStringNoAllowed values: xlsx, csv, json; Default: "xlsx"
includeAssetUrlsBooleanNoDefault: true
localeStringNoAllowed values: zh-CN, en-US, zh-TW, ja-JP, ko-KR, pt-BR, ru-RU, id-ID, fr-FR, it-IT, pl-PL, vi-VN, tr-TR, th-TH, de-DE, es-ES, hi-IN; Default: "zh-CN"

Response fields: 200. Success returns an attachment containing file bytes. Errors before delivery use JSON.

Response headers: Content-Disposition, X-Download-Record-Id, X-Idempotency-Replayed

Request example:

bash
curl --request POST \
  'https://open.musedam.cc/api/product/product-downloads' \
  --header 'Authorization: Bearer your_api_key' \
  --header 'Content-Type: application/json' \
  --data-raw '{
  "kind": "export",
  "productIds": [
    40
  ],
  "format": "xlsx",
  "includeAssetUrls": true,
  "locale": "zh-CN"
}' \
  --output products.xlsx
Response example
http
HTTP/1.1 200 OK
Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
Content-Disposition: attachment
X-Download-Record-Id: <record-id>

The response body contains the file bytes.

List product download candidates

Endpoint: /products/{id}/download-assets

Method: GET

Description: Returns current candidate selection keys and downloadable availability under read and download authorization. No business download record is created, and no original or preview URLs are returned. Use the single-file route or POST product-downloads to retrieve bytes.

Required role: usable, editable or manage.

Request parameters:

ParameterTypeRequiredDescription
idIntegerYesPath parameter. Positive signed 32-bit resource ID. Positive integer ID, maximum 2147483647.

Response fields: 200; data: List<ProductDownloadCandidate>. field details

Request example:

bash
curl --request GET \
  'https://open.musedam.cc/api/product/products/40/download-assets' \
  --header 'Authorization: Bearer your_api_key'
Response example
json
{
  "success": true,
  "data": [
    {
      "key": "p40:a2",
      "name": "example.png",
      "productId": 40,
      "productName": "Example product",
      "category": "main",
      "mimeType": "image/png",
      "size": "1024",
      "downloadable": true
    }
  ],
  "code": null,
  "message": null,
  "requestId": "11111111-1111-4111-8111-111111111111"
}

Download one product original

Endpoint: /products/{id}/download-assets/{assetKey}

Method: GET

Description: Fetches one current product original identified by an encoded candidate key. Requires a Bearer header and current read/download authorization; the URL contains no API-key secret. Creates a business record before delivery. Optional 24-hour idempotency is scoped separately from POST downloads, reuses the record, and still reauthorizes and transfers current bytes.

Required role: usable, editable or manage.

Request parameters:

ParameterTypeRequiredDescription
idIntegerYesPath parameter. Positive signed 32-bit resource ID. Positive integer ID, maximum 2147483647.
assetKeyStringYesPath parameter. URL-encoded selection key returned by the product download-candidate list. Not a FilePath or AssetObject ID. Minimum length 1 characters; Maximum length 256 characters
Idempotency-KeyStringNoOptional header. Retry the same key and input within 24 hours; see Idempotent retries.

Response fields: 200. Success returns an attachment containing file bytes. Errors before delivery use JSON.

Response headers: Content-Disposition, X-Download-Record-Id, X-Idempotency-Replayed

Request example:

The example uses the download-candidate key p40:a2, encoded as p40%3Aa2 in the URL. Use the key returned by your download-candidate list; it is not a FilePath or AssetObject ID.

bash
curl --request GET \
  'https://open.musedam.cc/api/product/products/40/download-assets/p40%3Aa2' \
  --header 'Authorization: Bearer your_api_key' \
  --output product-asset.bin
Response example
http
HTTP/1.1 200 OK
Content-Type: application/octet-stream
Content-Disposition: attachment
X-Download-Record-Id: <record-id>

The response body contains the file bytes.

MuseDAM Developer PlatformAPI · Integrations · MCP
    Product Library API | MuseDAM Developers