{"openapi":"3.0.0","info":{"title":"Afineo Webservices API — attributes","description":"Module `attributes` of the Afineo API v3 (17 endpoints).\n\nDefine and manage asset attributes (fields) within a library: create attributes, organize them into tabs and groups, assign them to types, and retrieve JSON schemas for validation. This is the core module for data model configuration.\n\nFull specification: https://api.afineo.io/openapi.json","version":"3.0.0","contact":{"name":"Afineo Solutions","url":"https://www.afineo.com"},"x-logo":{"url":"./logo.png","altText":"Afineo - Powerful Data, Powerful People"}},"servers":[{"url":"https://{instance}.afineo.io","description":"Afineo instance","variables":{"instance":{"description":"Your Afineo instance name","default":"client"}}}],"tags":[{"name":"attributes","description":"Define and manage asset attributes (fields) within a library: create attributes, organize them into tabs and groups, assign them to types, and retrieve JSON schemas for validation. This is the core module for data model configuration."}],"paths":{"/api/v3/attributes/assign-masks/":{"post":{"tags":["attributes"],"summary":"Sync display mask permissions for an attribute (add, update, remove)","description":"## Purpose\n\nSynchronizes display mask permissions for a single attribute. Send the **complete desired state** for all masks: the endpoint computes a diff and applies additions, updates, and removals automatically.\n\n## Library Context\n\nThe library (class_id) is automatically derived from the attribute — no class_id parameter is required.\n\n## Sync Behavior\n\n- Mask in request AND in DB → **updated** if permissions differ, **unchanged** if identical\n- Mask in request but NOT in DB → **added** (unless `permission: \"hidden\"`, which is a no-op)\n- Mask in DB but NOT in request → **removed**\n- Mask 10 (full display mask) is always ignored and cannot be modified\n\n## Partial Failures\n\nInvalid entries (unknown mask ID or label, missing fields, protected mask 10) do NOT fail the whole request — valid entries are still applied and `status_code` remains 1. All failures are reported in `result.errors`.\n\n## Permission Values\n\n- `\"hidden\"` — attribute is not visible in this mask (no entry in DB)\n- `\"read\"` — attribute is visible, read-only\n- `\"write\"` — attribute is visible and editable\n\n## locally_editable\n\nOnly applies when `permission: \"write\"`. When `true`, the attribute reference value can be edited locally in assetShare. Ignored for `hidden` and `read` (always stored as false).\n\n## Accepts IDs or Labels\n\n- `mask`: mask ID (integer or numeric string, e.g. `1024` or `\"1024\"`) or mask label (string, case-insensitive). Use `POST /api/v3/attributes/masks/` with the attribute id to discover currently assigned masks. To assign a mask not yet present on this attribute, use `POST /api/v3/permissions/masks/list/` to list all available masks with their IDs and labels.\n\n## Empty assignments\n\nSending `assignments: []` removes **all** mask permissions for this attribute (except mask 10, which is always protected). This is a destructive operation — use with care.\n\n## Related Endpoints\n\n- `attributes/masks` — read current mask permissions for an attribute\n- `permissions/masks/assign-attributes` — inverse: given one mask, sync all attributes of a library in that mask\n- `permissions/masks/list` — list all available display masks with IDs and labels\n- `attributes/assign-types` — sync type assignments for attributes in a library\n","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Sync result response","content":{"application/json":{"schema":{"oneOf":[{"type":"object","title":"Success Response","properties":{"status_code":{"type":"integer","example":1,"description":"Always 1 for this endpoint, even when some entries failed (partial failures are reported in result.errors, not via status_code)"},"status_message":{"type":"string","example":"2 added, 1 updated, 1 removed","description":"Summary of changes (e.g. \"2 added, 1 updated, 1 removed\"). Suffix \"(N error(s))\" is appended when result.errors is non-empty. \"No changes\" when nothing changed."},"result":{"type":"object","properties":{"attribute_id":{"type":"integer","example":231,"description":"Attribute ID"},"masks":{"type":"array","description":"Per-mask action details. Only covers masks that were in the request or in DB — not the full list of all masks in the system.","items":{"type":"object","properties":{"mask_id":{"type":"integer","example":3},"action":{"type":"string","enum":["added","updated","unchanged","removed"],"example":"added"},"permission":{"type":"string","enum":["read","write"],"description":"Absent when action is \"removed\". Always false for locally_editable when permission is \"read\".","example":"write"},"locally_editable":{"type":"boolean","description":"Absent when action is \"removed\". Always false when permission is \"read\".","example":true}}}},"total_added":{"type":"integer","example":2},"total_updated":{"type":"integer","example":1},"total_removed":{"type":"integer","example":1},"cache_refreshed":{"type":"boolean","example":true},"errors":{"type":"array","items":{"type":"string"},"description":"Per-entry errors that did not block execution. Valid entries are still applied.","example":["Mask not found: Masque inexistant","Mask 10 (full display mask) cannot be modified"]}}}},"required":["status_code","status_message","result"]},{"type":"object","title":"Error Response","properties":{"status_code":{"type":"integer","example":2,"description":"Status code (2 = error)"},"status_message":{"type":"string","example":"Attribute \"999\" does not exist","description":"Error message"},"result":{"type":"array","items":{"type":"object"},"example":[],"description":"Empty array on error"}},"required":["status_code","status_message","result"]}]}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"integer","description":"Attribute ID. Use POST /api/v3/attributes/list with class_id to get available attribute IDs.","example":231},"mode":{"type":"string","description":"Assignment mode. Controls ONLY whether masks absent from the list are removed. \"replace\" (default) = full SYNC: masks not in the list are REMOVED. \"add\" = non-destructive: masks absent from the list are KEPT. In BOTH modes, each mask you send is created if missing or UPDATED if already present with a different permission (upsert — NOT insert-only; identical = unchanged; never duplicated).","enum":["replace","add"],"example":"add"},"assignments":{"type":"array","description":"Complete desired mask state for this attribute. Each entry must have \"mask\" (integer ID or string label — use POST /api/v3/attributes/masks/ with the attribute id to get currently assigned mask IDs and labels) and \"permission\" (\"hidden\", \"read\", or \"write\"). Optionally include \"locally_editable\" (boolean, only effective with \"write\"). WARNING: this is a SYNC operation — masks in DB but not in this list will be REMOVED. Mask ID 10 (full display mask) is always protected — any entry targeting mask 10 will be ignored and reported in \"errors\". (Array of objects - see example for structure)","items":{"type":"object","properties":{"mask":{"oneOf":[{"type":"integer"},{"type":"string"}],"description":"Mask ID (integer or numeric string, e.g. 1024 or \"1024\") or mask label (string, case-insensitive, e.g. \"Masque commercial\"). Use POST /api/v3/attributes/masks/ with the attribute id to get currently assigned masks.","example":1024},"permission":{"type":"string","description":"Desired permission level for this attribute in the mask.","enum":["hidden","read","write"],"example":"write"},"locally_editable":{"type":"boolean","description":"Only effective when permission is \"write\". Defaults to false.","example":true}},"required":["mask","permission"],"title":"Item"}}},"required":["id","assignments"]}}}}}},"/api/v3/attributes/assign-types/":{"post":{"tags":["attributes"],"summary":"Sync type assignments for an attribute (add, update, remove)","description":"## Purpose\n\nSynchronizes which record types an attribute is assigned to, along with per-type display options. Send the **complete desired state** for this attribute: the endpoint computes a diff and applies additions, updates, and removals automatically.\n\n## Library Context\n\nThe library (class_id) is automatically derived from the attribute — no class_id parameter is required.\n\n## Sync Behavior\n\n- Type in the list but NOT currently assigned → **added**\n- Type currently assigned but NOT in the list → **removed**\n- Type in both → **updated** if options differ, **unchanged** if identical\n\nSending `assignments: []` removes the attribute from **all** types. Use with care.\n\n## Per-type options\n\nEach assignment entry supports two optional display options:\n\n**`editability`** — controls editability of the attribute value for this type:\n- `\"default\"` (default) — inherits the attribute's global editability setting\n- `\"read_only\"` — value is always read-only for this type, regardless of the global setting\n- `\"editable\"` — value is always editable for this type, regardless of the global setting\n\nOmitting `editability` is equivalent to `\"default\"`.\n\n**`hidden`** — hides the attribute in the interface for this type:\n- `false` (default) — attribute is visible\n- `true` — attribute is hidden in the interface for this type\n\nOmitting `hidden` is equivalent to `false`.\n\n## Partial Failures\n\nInvalid entries (unknown type ID or label, missing fields) do NOT fail the whole request — valid entries are still applied and `status_code` remains 1. All failures are reported in `result.errors`.\n\n## Accepts IDs or Labels\n\n`type`: integer (type ID) or string (type label, case-insensitive, use POST /api/v3/types/list with class_id to discover).\n\n## Related Endpoints\n\n- `attributes/types` — read current type assignments for an attribute\n- `types/assign-attributes` — inverse: given one type, sync all its attributes\n- `types/list` — list available types with their IDs and labels (requires class_id)\n\n## Complete workflow\n\n1. `attributes/tabs/create` - Create tab\n2. `attributes/groups/create` - Create group in tab\n3. `attributes/create` or `attributes/batch-create` - Create attribute(s)\n4. `attributes/assign-types` - Sync type assignments (you are here)\n","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Sync result response","content":{"application/json":{"schema":{"oneOf":[{"type":"object","title":"Success Response","properties":{"status_code":{"type":"integer","example":1,"description":"Always 1 for this endpoint, even when some entries failed (partial failures are reported in result.errors, not via status_code)"},"status_message":{"type":"string","example":"2 added, 1 updated, 1 removed","description":"Summary of changes (e.g. \"2 added, 1 updated, 1 removed\"). Suffix \"(N error(s))\" is appended when result.errors is non-empty. \"No changes\" when nothing changed."},"result":{"type":"object","properties":{"attribute_id":{"type":"integer","example":456,"description":"Attribute ID"},"types":{"type":"array","description":"Per-type action details. Only covers types that were in the request or currently assigned — not all types in the system.","items":{"type":"object","properties":{"type_id":{"type":"integer","example":12,"description":"Resolved type ID"},"action":{"type":"string","enum":["added","updated","unchanged","removed"],"example":"added","description":"Operation applied to this type assignment"},"editability":{"type":"string","enum":["default","read_only","editable"],"example":"default","description":"Reflects the value sent in the request (not the stored value). Absent when action is \"removed\"."},"hidden":{"type":"boolean","example":false,"description":"Reflects the value sent in the request (not the stored value). Absent when action is \"removed\"."}}}},"total_added":{"type":"integer","example":2},"total_updated":{"type":"integer","example":1},"total_removed":{"type":"integer","example":1},"cache_refreshed":{"type":"boolean","example":true},"errors":{"type":"array","items":{"type":"string"},"description":"Per-entry errors that did not block execution. Valid entries are still applied.","example":["Type not found: inexistant"]}}}},"required":["status_code","status_message","result"]},{"type":"object","title":"Error Response","properties":{"status_code":{"type":"integer","example":2,"description":"Status code 2 when attribute not found"},"status_message":{"type":"string","example":"Attribute \"999\" does not exist","description":"Error message"},"result":{"type":"array","items":{"type":"object"},"example":[],"description":"Empty array on error"}},"required":["status_code","status_message","result"]}]}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"integer","description":"Attribute ID. Use POST /api/v3/attributes/list with class_id to get available attribute IDs.","example":456},"mode":{"type":"string","description":"Assignment mode. Controls ONLY whether types absent from the list are removed. \"replace\" (default) = full SYNC: types not in the list are REMOVED. \"add\" = non-destructive: types absent from the list are KEPT. In BOTH modes, each type you send is created if missing or UPDATED if already present with different options (upsert — NOT insert-only; identical options = unchanged; never duplicated).","enum":["replace","add"],"example":"add"},"assignments":{"type":"array","description":"Complete desired type state for this attribute. Each entry must have \"type\" (integer ID or string label). Optional: \"editability\" (\"default\", \"read_only\", \"editable\") and \"hidden\" (boolean). WARNING: this is a SYNC operation — types not in this list will be REMOVED. (Array of objects - see example for structure)","example":[{"type":"PROJECTEURS","editability":"editable","hidden":false},{"type":12,"editability":"default","hidden":true}],"items":{"type":"object","properties":{"type":{"oneOf":[{"type":"integer"},{"type":"string"}],"description":"Type ID (integer) or type label (string, case-insensitive). Use POST /api/v3/types/list with class_id to discover.","example":"PROJECTEURS"},"editability":{"type":"string","description":"Editability override for this type. Defaults to \"default\" (inherits global setting).","enum":["default","read_only","editable"],"example":"editable"},"hidden":{"type":"boolean","description":"Hide attribute in interface for this type. Defaults to false.","example":false}},"required":["type"],"title":"Item"}}},"required":["id","assignments"]}}}}}},"/api/v3/attributes/batch-create/":{"post":{"tags":["attributes"],"summary":"Create multiple attributes in batch","description":"## Purpose\n\nCreates multiple attributes in a single call with cache revalidation only at the end, improving performance for bulk operations.\n\n## Workflow\n\n1. Call POST /api/v3/attributes/tabs/list and POST /api/v3/attributes/groups/list to get available tabs and groups\n2. Call POST /api/v3/attributes/schema/ for each data_type you plan to use\n3. Prepare your attributes array — each attribute requires `type` (\"simple_value\" or \"advanced_value\"), optionally with `types` and `masks` per attribute\n4. Call this endpoint\n\n## Behavior\n\n- All attributes are created in the same library (`class_id` at root level)\n- Each attribute is validated independently (same rules as POST /api/v3/attributes/create/)\n- If an attribute fails validation, it is skipped and reported in `errors` — other attributes are still created\n- Cache is revalidated only once after all attributes are created, which is significantly faster for large batches\n- If `types` is provided, type assignments are applied after creation using the same sync logic as POST /api/v3/attributes/assign-types/. Since the attribute is new, this is purely additive (no prior state to remove)\n- If `masks` is provided, mask permissions are applied after creation using the same sync logic as POST /api/v3/attributes/assign-masks/. Since the attribute is new, this is purely additive\n- Invalid entries inside `types` or `masks` (unknown ID/label, missing required fields) do NOT abort the attribute creation — the attribute is still created and per-entry errors are reported in `created[i].types.errors` / `created[i].masks.errors`\n\n## Status Codes\n\n- `1`: All attributes created successfully\n- `2`: Partial success — some attributes created, some failed (check `errors` array)\n- `3`: All attributes failed (creation failures only — `status_code` does not reflect `types`/`masks` assignment errors; always check `created[i].types.errors` / `created[i].masks.errors` for partial assignment failures)\n\n## When to use inline `types`/`masks` vs separate endpoints\n\n- **Use inline** when creating attributes that already need to be assigned to specific record types or masks — saves N×2 roundtrips per attribute compared to calling `assign-types`/`assign-masks` separately\n- **Use separate endpoints** (`POST /api/v3/attributes/assign-types/`, `POST /api/v3/attributes/assign-masks/`) when updating assignments on an existing attribute, or when the final assignment state is not known at creation time\n\n## Example Call\n\n```json\n{\n  \"class_id\": 4,\n  \"attributes\": [\n    {\n      \"tab_id\": 105,\n      \"data_type\": \"short_text\",\n      \"label\": \"Product Name\",\n      \"code\": \"product_name\",\n      \"type\": \"advanced_value\",\n      \"configuration\": {\n        \"required\": 1,\n        \"multilingual\": 1\n      },\n      \"types\": [\n        {\"type\": 12},\n        {\"type\": 15, \"editability\": \"read_only\"}\n      ],\n      \"masks\": [\n        {\"mask\": 1, \"permission\": \"write\"},\n        {\"mask\": 2, \"permission\": \"read\"}\n      ]\n    },\n    {\n      \"tab_id\": 105,\n      \"data_type\": \"integer\",\n      \"label\": \"Weight\",\n      \"code\": \"product_weight\",\n      \"type\": \"simple_value\",\n      \"configuration\": {\n        \"indexed\": 1\n      }\n    },\n    {\n      \"tab_id\": 106,\n      \"data_type\": \"dropdown_items\",\n      \"label\": \"Color\",\n      \"code\": \"product_color\",\n      \"type\": \"advanced_value\",\n      \"configuration\": {\n        \"list_values\": 12\n      }\n    }\n  ]\n}\n```\n","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Batch create response","content":{"application/json":{"schema":{"type":"object","properties":{"status_code":{"type":"integer","example":1,"description":"Status code (1 = all success, 2 = partial success, 3 = all failed)"},"status_message":{"type":"string","example":"3 attribute(s) created successfully","description":"Summary message"},"result":{"type":"object","properties":{"created":{"type":"array","description":"Array of successfully created attributes","items":{"type":"object","properties":{"id":{"type":"integer","description":"Created attribute ID"},"code":{"type":"string","description":"Attribute code"},"label":{"type":"string","description":"Attribute label"},"data_type":{"type":"string","description":"Attribute data type"},"class_id":{"type":"integer","description":"Library ID"},"tab_id":{"type":"integer","description":"Tab ID"},"field_name":{"type":"string","nullable":true,"description":"Database field name"},"types":{"type":"object","nullable":true,"description":"Present only if `types` was provided in the request. Absent otherwise.","properties":{"attribute_id":{"type":"integer","description":"Attribute ID"},"types":{"type":"array","description":"Per-type action results","items":{"type":"object","properties":{"type_id":{"type":"integer"},"action":{"type":"string","enum":["added","unchanged"]},"editability":{"type":"string"},"hidden":{"type":"boolean"}}}},"total_added":{"type":"integer"},"total_updated":{"type":"integer"},"total_removed":{"type":"integer"},"cache_refreshed":{"type":"boolean"},"errors":{"type":"array","description":"Per-entry errors (invalid type IDs/labels, missing fields). Attribute creation is NOT aborted by these errors.","items":{"type":"string"}}}},"masks":{"type":"object","nullable":true,"description":"Present only if `masks` was provided in the request. Absent otherwise.","properties":{"attribute_id":{"type":"integer","description":"Attribute ID"},"masks":{"type":"array","description":"Per-mask action results","items":{"type":"object","properties":{"mask_id":{"type":"integer"},"action":{"type":"string","enum":["added","updated","removed","unchanged"]},"permission":{"type":"string"},"locally_editable":{"type":"boolean"}}}},"total_added":{"type":"integer"},"total_updated":{"type":"integer"},"total_removed":{"type":"integer"},"cache_refreshed":{"type":"boolean"},"errors":{"type":"array","description":"Per-entry errors (invalid mask IDs/labels, missing fields). Attribute creation is NOT aborted by these errors.","items":{"type":"string"}}}}}}},"errors":{"type":"array","description":"Array of failed attributes with error details","items":{"type":"object","properties":{"index":{"type":"integer","description":"Index in the original attributes array"},"code":{"type":"string","nullable":true,"description":"Attribute code (if provided)"},"label":{"type":"string","nullable":true,"description":"Attribute label (if provided)"},"error":{"type":"string","description":"Error message"}}}},"total_requested":{"type":"integer","description":"Total number of attributes requested"},"total_created":{"type":"integer","description":"Number of successfully created attributes"},"total_errors":{"type":"integer","description":"Number of failed attributes"}}}},"required":["status_code","status_message","result"]}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"class_id":{"type":"integer","description":"Library ID where all attributes will be created. Use POST /api/v3/administration/classes/list to get available libraries.","example":4},"attributes":{"type":"array","description":"Array of attribute objects to create. Each object has the same structure as POST /api/v3/attributes/create/ parameters (except class_id which is defined at root level). Each object MUST include \"type\" (\"simple_value\" or \"advanced_value\"): it is required per attribute and determines the simple/advanced nature (some data_types force \"advanced_value\"). (Array of objects - see example for structure)","example":[{"tab_id":105,"data_type":"short_text","label":"Product Name","code":"product_name","configuration":{"required":1,"multilingual":1},"types":[{"type":12},{"type":15,"editability":"read_only"}],"masks":[{"mask":1,"permission":"write"},{"mask":2,"permission":"read"}]},{"tab_id":105,"data_type":"integer","label":"Weight","code":"product_weight"}],"items":{"type":"object","properties":{"tab_id":{"type":"integer","description":"Tab ID where the attribute will be placed. Must belong to the specified class_id (validated server-side). Use POST /api/v3/attributes/tabs/list with class_id to get available tabs.","example":105},"data_type":{"type":"string","description":"The attribute data type. Determines available configuration properties. Common types: short_text, dropdown_items, integer, decimal, boolean, date, asset_link, rich_text. Call POST /api/v3/attributes/schema/ to get the schema for a specific data_type.","enum":["afs","asset_link","boolean","checkbox","counter","data_table","date","datetime","decimal","dropdown_items","dropdown_items_editable","dynamic_afs","dynamic_link","ean","email","html","integer","long_text","multi_dropdown_items","passthrough","price","product_table","radio_items","rich_text","short_text","splitter","url"],"example":"short_text"},"label":{"type":"string","description":"Display label for the attribute. Maximum 255 characters.","example":"Product Name"},"code":{"type":"string","description":"Unique code/identifier (XML code). Must start with a letter, contain only alphanumeric characters and underscores. Must be globally unique across all libraries.","example":"product_name"},"type":{"type":"string","enum":["simple_value","advanced_value"],"description":"Required per attribute. Value nature: \"simple_value\" (stored on the asset) or \"advanced_value\" (multiversion). Some data_types force \"advanced_value\" (links, afs, items lists...). \"advanced_value\" requires the library to support advanced values.","example":"simple_value"},"group_id":{"type":"integer","description":"Group ID within the tab. Must belong to the specified tab_id. Omit to place the attribute outside any group.","example":81},"description":{"type":"string","description":"Description of the attribute. Maximum 500 characters.","example":"The main product name displayed to customers"},"configuration":{"type":"object","description":"Type-specific configuration properties. Available properties depend on the data_type. Key properties by type: dropdown_items/multi_dropdown_items/radio_items require list_values; asset_link/dynamic_link require class_linked_id; afs requires afs (formula). Use POST /api/v3/attributes/schema/ for the complete schema per data_type.","example":{"required":1,"multilingual":1,"character_limit":"0-255"}},"types":{"type":"array","description":"Optional. Record type assignments to apply after attribute creation. Each entry requires a `type` field (integer ID or string label from POST /api/v3/types/list). The `type` field accepts either an integer ID or a string label.","items":{"type":"object","properties":{"type":{"description":"Type ID (integer) or type label (string). Use POST /api/v3/types/list to get available types.","example":12},"editability":{"type":"string","enum":["default","read_only","editable"],"description":"Editability override for this type. Omit for library default."},"hidden":{"type":"boolean","description":"Set to true to hide this attribute in this type."}},"required":["type"]},"example":[{"type":12},{"type":15,"editability":"read_only","hidden":false}]},"masks":{"type":"array","description":"Optional. Display mask permission assignments to apply after attribute creation. Since the attribute is new, this is purely additive (same sync logic as POST /api/v3/attributes/assign-masks/ but no prior state to remove). Each entry requires a `mask` field (integer ID or string label) and a `permission` field.","items":{"type":"object","properties":{"mask":{"description":"Mask ID (integer) or mask label (string).","example":1},"permission":{"type":"string","enum":["hidden","read","write"],"description":"Permission level: \"hidden\" = not visible, \"read\" = read-only, \"write\" = editable."},"locally_editable":{"type":"boolean","description":"Only applicable when permission is \"write\". Allows editing of locally-inherited values."}},"required":["mask","permission"]},"example":[{"mask":1,"permission":"write"},{"mask":2,"permission":"read"}]}},"required":["tab_id","data_type","label","code","type"]}}},"required":["class_id","attributes"]}}}}}},"/api/v3/attributes/create/":{"post":{"tags":["attributes"],"summary":"Create a new attribute","description":"## Purpose\n\nCreates a new attribute with validation against the JSON schema for the specified data_type.\n\n## Workflow\n\n1. First call POST /api/v3/attributes/schema/ with your desired data_type to get the schema\n2. Prepare your attribute data according to the schema requirements\n3. Call this endpoint with common properties at root level and type-specific properties in `configuration`\n\n## Parameter Structure\n\nParameters are organized in two levels:\n- **Root level**: Common properties required for all data types\n- **configuration**: Object containing type-specific properties (varies by data_type)\n\n## Required Fields (all data types)\n\n- `class_id`: Library ID where the attribute will be created\n- `tab_id`: Tab ID where the attribute will be placed\n- `data_type`: The attribute data type (determines available configuration properties)\n- `label`: Display label for the attribute\n- `code`: Unique code/identifier (must start with a letter, globally unique across all libraries)\n- `type`: Value nature — \"simple_value\" or \"advanced_value\" (see below)\n\n## Simple vs Advanced Value (`type`)\n\n`type` is chosen at creation and is **immutable** afterwards (it cannot be changed via update):\n- `simple_value`: value stored directly on the asset. Cannot use multilingual, inheritance (`inheritable`/`default_inheritance`); always scope_id 1 (reference) — scope is not configurable for simple values.\n- `advanced_value`: multiversion value. Required for multilingual, inheritance, and contextual/overloadable scopes (scope_id 2, 4, 5).\n\nSome data types force `advanced_value` (links, afs, dynamic_afs, items lists, product_table, characteristic, etim, splitter, multi_dropdown): sending `simple_value` for those is rejected. `advanced_value` on a \"classic\" type requires the library to support advanced (multiversion) values.\n\n## Type-Specific Requirements (in configuration)\n\nSome data types require additional fields in the configuration object:\n- `asset_link`, `dynamic_link`: requires `class_linked_id` (target library ID, use POST /api/v3/administration/classes/list to get available IDs)\n- `dynamic_link`: also requires `dynamic_afs` (AfineoScript for dynamic search)\n- `dropdown_items`, `dropdown_items_editable`, `multi_dropdown_items`, `radio_items`: requires `list_values` (values list ID, must belong to same class_id - get IDs via POST /api/v3/listvalues/list)\n- `afs`: requires `afs` (AfineoScript formula, e.g. `{ref:product_name} - {ref:brand}`)\n- `counter`: accepts `counter_start` (integer, starting value, default 0), `counter_afs` (string, AfineoScript for counter format), `counter_nbcharacters` (integer, zero-padding character count)\n- See schema endpoint for complete requirements per data_type\n\n## Default Value: static vs AFS-computed (mutually exclusive)\n\nAn attribute can have **at most one** default value, in **one of two modes** — never both:\n\n| Mode | How to set it | Behavior |\n|---|---|---|\n| **Static** default | `default_value` = \"<fixed text>\" (and `default_value_afs` = 0 or omitted) | The fixed value is prefilled on new records. |\n| **AFS-computed** default | `default_value_afs` = 1 **and** `afs` = \"<AfineoScript>\" (do **not** send `default_value`) | When a record has no value, the `afs` script is evaluated to produce the default (\"computed default when empty\"). |\n\n- These modes are **mutually exclusive**: sending `default_value` together with `default_value_afs=1` is **rejected** (it would be ambiguous). Choose one.\n- **To switch AFS-computed → static**: send `default_value_afs=0` **with** `default_value`. Sending `default_value` alone does not clear an existing AFS default.\n- ⚠️ Do not confuse this scalar `afs` (a *default value*) with the `afs` / `dynamic_afs` **data types**, where `afs` is its **core formula** (always computed, not a fallback default).\n\n## Type-Specific Restrictions\n\nSome data types have restrictions on configuration properties:\n- `splitter`: indexed and editable are forbidden (visual separator only)\n- `counter`: editable is forbidden (auto-incremented)\n- `multi_dropdown_items`: multilingual is forbidden (managed at list level)\n- `passthrough`: required, unique, indexed, default_value, character_limit are forbidden (display-only)\n- `inheritable` and `default_inheritance` are mutually exclusive (cannot both be true)\n- `ai_completion` is only available for short_text, long_text, and rich_text\n- `create_value` and `display_assets` are only available for asset_link and dynamic_link\n- `asset_multiple` requires `display_assets` to be 2\n- `display_assets` = 0 forbids `display_assets_groupby` and `display_assets_attributes` (asset_link, dynamic_link)\n- `display_assets_attributes` (table mode): **ordered array of attribute IDs of the linked library** to show as columns (order = column order), e.g. `[732, 737]`. `display_assets_groupby`: attribute ID to group rows by. Both require `display_assets` >= 1. Sending an empty `display_assets_attributes` is a no-op (columns unchanged).\n- `search` = 0 (search disabled) forbids `search_context`, `search_advanced`, `search_afs`, `search_advanced_freeze`, `dynamic_search_context`; `search` = 2 forbids `search_advanced` (asset_link, dynamic_link)\n- `remain_visible_master_asset` requires `property` to be 2\n- `multilingual_default_value` requires `multilingual` to be 1\n- When `default_value_afs` is 1, `afs` (AfineoScript formula) is required\n- `default_value` (static default) and `default_value_afs`=1 (AFS-computed default) are **mutually exclusive** — sending both is rejected (see \"Default Value\" section above)\n- `afs` is only allowed when `default_value_afs` is 1 for data types other than `afs`/`dynamic_afs` (where `afs` is the core formula)\n- For `type` = `simple_value`: `multilingual`, `multilingual_default_value`, `inheritable`, `default_inheritance`, `ai_completion` are forbidden, and `scope_id` must be 1 (scope is only configurable for advanced values)\n- `scope_id` values: 1 (reference), 2 (overloadable), 3 (locally editable), 4 (contextual), 5 (workspace). **Scope 5 requires the `ATTRIBUT_WORKSPACE` extension** (without it, only 1-4 are allowed).\n- `scope_id` change (update only): cannot be changed on a **reciprocal** attribute. Otherwise, \"free\" transitions without cleaning are 1->2/3 and 3->1. Any other change is allowed **only if the attribute has no values in records**; if records already hold values, they must be cleaned first (via the edit UI) before switching to that scope.\n\n> Note: conditional rules declared in each data_type JSON schema (`allOf`) are enforced automatically at create/update, in addition to the rules above.\n\n## Crossed Data Attributes\n\nSet `is_crossed_data: true` in configuration to make the attribute a crossed data attribute.\n\n**Requirements**:\n- The library must support crossed data\n- Forbidden data_types: afs, dynamic_afs, splitter, etim, characteristic\n\n**Forbidden properties** (automatically disabled when is_crossed_data is true):\nrequired, unique, default_value, default_value_afs, multilingual, property, inheritable, default_inheritance, alignment, character_limit, help, ai_completion, create_value, search, search_context, dependency, dynamic_afs, display_assets, usage, counter_start, counter_afs.\n\n## Example Call\n\n```json\n{\n  \"class_id\": 4,\n  \"tab_id\": 105,\n  \"data_type\": \"short_text\",\n  \"label\": \"Product Name\",\n  \"code\": \"product_name\",\n  \"type\": \"advanced_value\",\n  \"configuration\": {\n    \"required\": 1,\n    \"multilingual\": 1,\n    \"unique\": 1,\n    \"character_limit\": \"0-255\"\n  }\n}\n```\n\n## Example: Create a dropdown_items attribute\n\n```json\n{\n  \"class_id\": 4,\n  \"tab_id\": 105,\n  \"data_type\": \"dropdown_items\",\n  \"label\": \"Color\",\n  \"code\": \"product_color\",\n  \"type\": \"advanced_value\",\n  \"configuration\": {\n    \"list_values\": 12,\n    \"required\": 1\n  }\n}\n```\n\n## Common Errors\n\n- `Class ID \"X\" is invalid` - the specified class_id does not exist or is not active\n- `class_linked_id is required for data type \"X\"` - asset_link and dynamic_link types require a target library\n- `Tab not found: X` - the specified tab_id does not exist\n- `Tab X does not belong to class Y` - tab_id must belong to the specified class_id\n- `Group not found: X` - the specified group_id does not exist\n- `Group X does not belong to tab Y` - group_id must belong to the specified tab_id\n- `Values list not found: X` - the specified list_values does not exist\n- `Values list X does not belong to class Y` - list_values in configuration must belong to the same class_id\n- `Target class (class_linked_id) not found: X` - the specified class_linked_id does not exist\n- `Attribute code already exists: X` - code must be globally unique across all libraries\n- `Invalid code format` - code must match ^[a-zA-Z][a-zA-Z0-9_]*$\n- `Label exceeds maximum length of 255 characters` - label must be <= 255 chars\n- `Description exceeds maximum length of 500 characters` - description must be <= 500 chars\n- `Help exceeds maximum length of 1000 characters` - help (in configuration) must be <= 1000 chars\n- `Library X does not accept crossed data attributes` - library must support crossed data\n- `Data type X is not compatible with crossed data attributes` - some types are forbidden for crossed data\n- `\"X\" is not allowed for crossed data attributes` - property X is forbidden when is_crossed_data is true\n- `\"list_values\" is required for data type \"X\"` - dropdown_items, dropdown_items_editable, multi_dropdown_items, radio_items need list_values\n- `\"afs\" is required for data type \"afs\"` - afs type needs the afs formula\n- `\"dynamic_afs\" is required for data type \"dynamic_link\"` - dynamic_link type needs the dynamic_afs script\n- `\"X\" is not available for data type \"Y\"` - property X is not allowed for the specified data_type\n- `Invalid \"character_limit\" format` - must be in \"min-max\" format (e.g., \"0-255\")\n- `Invalid \"character_limit\": minimum cannot be greater than maximum` - min value must be <= max value\n- `\"ai_completion\" is only available for short_text, long_text, and rich_text` - type restriction\n- `\"create_value\" is only available for asset_link and dynamic_link` - type restriction\n- `\"display_assets\" is only available for asset_link and dynamic_link` - type restriction\n- `\"asset_multiple\" requires \"display_assets\" to be 2` - dependency constraint\n- `\"remain_visible_master_asset\" requires \"property\" to be 2` - dependency constraint\n- `\"multilingual_default_value\" requires \"multilingual\" to be 1` - dependency constraint\n- `When \"default_value_afs\" is 1, \"afs\" is required` - dependency constraint\n- `\"default_value\" (static default) and \"default_value_afs\" (AFS-computed default) are mutually exclusive` - a default value is either static or AFS-computed, not both; send only one\n- `scope_id is only configurable for advanced values` - a simple value is always scope 1; use type=advanced_value to set a scope\n- `scope_id change from X to Y is locked: N record(s) have a value...` (update) - free transitions are 1->2/3 and 3->1; other changes need the attribute to have no values (clean them first)\n- `scope_id cannot be modified for reciprocal attributes` (update) - reciprocal attributes have a fixed scope\n- `scope_id 5 (workspace) requires the ATTRIBUT_WORKSPACE extension` - without the extension, only scopes 1-4 are available\n- `scope_id is not configurable for this library (asset share is active). Only scope 1 (reference) is available.` - the library must have asset share deactivated to configure a scope\n- `suffix_list and suffix_text cannot be set at the same time` - choose either a list-based suffix or a text suffix, not both\n- `Suffix values list not found: X` / `Suffix values list X does not belong to class Y` - suffix_list must reference an existing values list of the same library\n- `AFS syntax error in \"<field>\": ...` - an AfineoScript field (afs, dependency, help, counter_afs, search_afs, dynamic_afs, smart_search) has invalid syntax\n- `Parameter \"type\" is required and must be \"simple_value\" or \"advanced_value\"` - type is mandatory at creation\n- `Value for \"type\" must be: advanced_value` - this data_type always forces an advanced value; simple_value is not allowed\n- `type \"advanced_value\" is not available: library X does not support advanced (multiversion) values` - the library must support advanced values\n- `\"X\" is not allowed when \"type\" is simple_value` - property X (multilingual, inheritance...) requires an advanced value\n- `\"X\" is not allowed when \"search\" is 0` - search-related properties require search enabled (asset_link/dynamic_link)\n- `\"search_advanced\" is not allowed when \"search\" is 2` - search_advanced is only valid when search = 1\n- `\"X\" is not allowed when \"display_assets\" is 0` - display_assets sub-options require display_assets > 0\n- `\"afs\" can only be set when \"default_value_afs\" is 1 for data type \"X\"` - for non-afs types, afs is the computed default value\n- `Property \"X\" is not available for data type \"Y\"` - property X is not part of the schema of data_type Y\n- `counter_start must be >= 0` - counter starting value must be non-negative\n- `counter_nbcharacters must be >= 0` - counter zero-padding must be non-negative\n","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Create attribute response","content":{"application/json":{"schema":{"oneOf":[{"type":"object","title":"Success Response","properties":{"status_code":{"type":"integer","example":1,"description":"Status code (1 = success)"},"status_message":{"type":"string","example":"Attribute created successfully","description":"Success message"},"result":{"type":"object","properties":{"id":{"type":"integer","example":456,"description":"ID of the created attribute"},"code":{"type":"string","example":"product_name","description":"Attribute code"},"label":{"type":"string","example":"Product Name","description":"Attribute label"},"data_type":{"type":"string","example":"short_text","description":"Attribute data type"},"class_id":{"type":"integer","example":4,"description":"Library ID"},"tab_id":{"type":"integer","example":105,"description":"Tab ID"},"field_name":{"type":"string","nullable":true,"example":"role_456","description":"Database field name (for simple_value types)"},"is_crossed_data":{"type":"boolean","nullable":true,"example":true,"description":"Present and true only when the attribute was created as crossed data"}},"required":["id","code","label","data_type","class_id","tab_id"]}},"required":["status_code","status_message","result"]},{"type":"object","title":"Validation Error Response","properties":{"status_code":{"type":"integer","example":2,"description":"Status code (2 = validation error)"},"status_message":{"type":"string","example":"Validation failed: class_linked_id is required for data_type asset_link","description":"Validation error message"},"result":{"type":"array","items":{"type":"object"},"example":[],"description":"Empty array on error"}},"required":["status_code","status_message","result"]},{"type":"object","title":"Error Response","properties":{"status_code":{"type":"integer","example":3,"description":"Status code (3 = creation error)"},"status_message":{"type":"string","example":"Error creating attribute: Attribute code already exists","description":"Error message"},"result":{"type":"array","items":{"type":"object"},"example":[],"description":"Empty array on error"}},"required":["status_code","status_message","result"]}]}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"class_id":{"type":"integer","description":"Library ID where the attribute will be created. Use POST /api/v3/administration/classes/list to get available libraries.","example":4},"tab_id":{"type":"integer","description":"Tab ID where the attribute will be placed. Must belong to the specified class_id (validated server-side). Use POST /api/v3/attributes/tabs/list with class_id to get available tabs for that library.","example":105},"data_type":{"type":"string","description":"The attribute data type. Determines available configuration properties. Common types: short_text (free text), dropdown_items (select from list), integer, decimal, boolean, date, asset_link (link to another library), rich_text (HTML content). After choosing a type, call POST /api/v3/attributes/schema/ with this data_type to get required configuration properties.","enum":["afs","asset_link","boolean","checkbox","counter","data_table","date","datetime","decimal","dropdown_items","dropdown_items_editable","dynamic_afs","dynamic_link","ean","email","html","integer","long_text","multi_dropdown_items","passthrough","price","product_table","radio_items","rich_text","short_text","splitter","url"],"example":"short_text"},"label":{"type":"string","description":"Display label for the attribute. Shown in forms and listings. Maximum 255 characters.","example":"Product Name"},"code":{"type":"string","description":"Unique code/identifier for the attribute (XML code). Must start with a letter and contain only alphanumeric characters and underscores (regex: ^[a-zA-Z][a-zA-Z0-9_]*$). Must be globally unique across all libraries. Example: product_name, ean_code.","example":"product_name"},"type":{"type":"string","description":"Value nature of the attribute (chosen at creation, immutable afterwards). \"simple_value\" = stored directly on the asset; \"advanced_value\" = multiversion value (required for contextual/overloadable scopes, multilingual, inheritance). Some data_types force \"advanced_value\" (links, afs, items lists...): sending \"simple_value\" for those is rejected. \"advanced_value\" requires the library to support advanced values.","enum":["simple_value","advanced_value"],"example":"simple_value"},"group_id":{"type":"integer","description":"Group ID within the tab. Must belong to the specified tab_id (validated server-side). Use POST /api/v3/attributes/groups/list with class_id and optionally tab_id to get available groups. Omit to place the attribute outside any group.","example":81},"description":{"type":"string","description":"Description of the attribute. Provides context for users filling the form. Maximum 500 characters.","example":"The main product name displayed to customers"},"configuration":{"type":"object","description":"Type-specific configuration properties. Available properties depend on the data_type. Key properties by type: dropdown_items/dropdown_items_editable/multi_dropdown_items/radio_items require list_values (must belong to same class_id, get IDs via POST /api/v3/listvalues/list); asset_link/dynamic_link require class_linked_id (target library ID); dynamic_link also requires dynamic_afs; afs requires afs (formula). Use POST /api/v3/attributes/schema/ to get the complete schema for a specific data_type.","example":{"required":1,"multilingual":1,"unique":1,"character_limit":"0-255"}}},"required":["class_id","tab_id","data_type","label","code","type"]}}}}}},"/api/v3/attributes/full/":{"post":{"tags":["attributes"],"summary":"Get all attributes with full configuration","description":"## Purpose\n\nReturns complete information about all attributes of a class with full configuration.\n\n## Structure\n\nCombines `attributes/list` envelope (`results_total`, `items`) with `attributes/get` detail level (common properties + `configuration` object).\n","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Full attributes information response","content":{"application/json":{"schema":{"oneOf":[{"type":"object","title":"Success Response","properties":{"status_code":{"type":"integer","example":1,"description":"Status code (1 = success)"},"status_message":{"type":"string","example":"","description":"Empty string on success"},"result":{"type":"object","properties":{"results_total":{"type":"integer","example":25,"description":"Total number of attributes"},"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"integer","example":231,"description":"Attribute ID"},"code":{"type":"string","example":"product_name","description":"Attribute code"},"label":{"type":"string","example":"Product name","description":"Attribute label"},"tags":{"type":"array","items":{"type":"string"},"example":["tag1","tag2"],"description":"Array of tags (can be empty)"},"class_id":{"type":"integer","example":4,"description":"Library ID. Use POST /api/v3/administration/classes/list to get available libraries."},"description":{"type":"string","nullable":true,"example":"Product name description","description":"Attribute description"},"tab_id":{"type":"integer","example":105,"description":"Tab ID. Use POST /api/v3/attributes/tabs/list with class_id to get available tabs."},"tab_label":{"type":"string","example":"General","description":"Tab label"},"group_id":{"type":"integer","nullable":true,"example":81,"description":"Group ID. Use POST /api/v3/attributes/groups/list with class_id to get available groups."},"group_label":{"type":"string","nullable":true,"example":"Information","description":"Group label"},"data_type":{"type":"string","example":"short_text","enum":["afs","asset_link","boolean","characteristic","checkbox","counter","data_table","date","datetime","decimal","dropdown","dropdown_editable","dropdown_items","dropdown_items_editable","dynamic_afs","dynamic_link","ean","email","etim","html","integer","long_text","multi_dropdown","multi_dropdown_items","passthrough","price","product_table","radio","radio_items","rich_text","short_text","splitter","tiles","tiles_required","url"],"description":"Data type"},"type":{"type":"string","enum":["simple_value","advanced_value"],"example":"simple_value","description":"Attribute type (simple_value or advanced_value)"},"order":{"type":"integer","example":5,"description":"Display order"},"view_asset":{"type":"boolean","example":true,"description":"Whether attribute is visible on the asset"},"dependency":{"type":"string","nullable":true,"example":"{if #153#}1{/if}","description":"Attribute dependencies (AfineoScript)"},"scope":{"type":"string","example":"reference_value","enum":["reference_value","overloadable_repository_value","reference_value_directly_editable_locally","contextual_value","shared_value_to_project_group"],"description":"Attribute scope"},"langs_label":{"type":"object","additionalProperties":{"type":"string"},"example":{"Français (fr)":"Nom du produit","English (en)":"Product name"},"description":"Label translations by language"},"configuration":{"type":"object","description":"Specific configuration based on data_type. Properties vary depending on the attribute type.","additionalProperties":true,"example":{"name":"role_231","system":false,"required":true,"multilingual":true,"editable":true,"indexed":false,"permissions":[],"asset_types":[]}}},"required":["id","code","label","tags","class_id","data_type","type","order","view_asset","scope","langs_label","configuration"]},"description":"Array of attributes with full configuration"}},"required":["results_total","items"]}},"required":["status_code","status_message","result"]},{"type":"object","title":"Error Response","properties":{"status_code":{"type":"integer","example":2,"description":"Status code (2 = not found)"},"status_message":{"type":"string","example":"Class \"4\" does not exist","description":"Error message with class ID"},"result":{"type":"array","items":{"type":"object"},"example":[],"description":"Empty array on error"}},"required":["status_code","status_message","result"]}]}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"class_id":{"type":"integer","description":"Library ID. Use POST /api/v3/administration/classes/list to get available libraries.","example":4}},"required":["class_id"]}}}}}},"/api/v3/attributes/get/":{"post":{"tags":["attributes"],"summary":"Get attribute configuration","description":"## Purpose\n\nReturns complete field definition (type, validation rules, list values, display settings, permissions).\n\n## Use Cases\n\n- Understand attribute behavior before data submission\n- Build dynamic forms based on attribute configuration\n- Validate data format requirements\n","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Attribute information response","content":{"application/json":{"schema":{"oneOf":[{"type":"object","title":"Success Response","properties":{"status_code":{"type":"integer","example":1,"description":"Status code (1 = success)"},"status_message":{"type":"string","example":"","description":"Empty string on success"},"result":{"type":"object","description":"Attribute information with common properties at first level and specific properties in configuration","properties":{"id":{"type":"integer","example":231,"description":"Attribute ID"},"code":{"type":"string","example":"product_name","description":"Attribute code"},"label":{"type":"string","example":"Product name","description":"Attribute label"},"tags":{"type":"array","items":{"type":"string"},"example":["tag1","tag2"],"description":"Array of tags (can be empty)"},"class_id":{"type":"integer","example":4,"description":"Library ID. Use POST /api/v3/administration/classes/list to get available libraries."},"description":{"type":"string","nullable":true,"example":"Product name description","description":"Attribute description"},"tab_id":{"type":"integer","example":105,"description":"Tab ID. Use POST /api/v3/attributes/tabs/list with class_id to get available tabs."},"tab_label":{"type":"string","example":"General","description":"Tab label"},"group_id":{"type":"integer","nullable":true,"example":81,"description":"Group ID. Use POST /api/v3/attributes/groups/list with class_id to get available groups."},"group_label":{"type":"string","nullable":true,"example":"Information","description":"Group label"},"data_type":{"type":"string","example":"short_text","enum":["afs","asset_link","boolean","characteristic","checkbox","counter","data_table","date","datetime","decimal","dropdown","dropdown_editable","dropdown_items","dropdown_items_editable","dynamic_afs","dynamic_link","ean","email","etim","html","integer","long_text","multi_dropdown","multi_dropdown_items","passthrough","price","product_table","radio","radio_items","rich_text","short_text","splitter","tiles","tiles_required","url"],"description":"Data type"},"type":{"type":"string","enum":["simple_value","advanced_value"],"example":"advanced_value","description":"Attribute type (simple_value or advanced_value)"},"order":{"type":"integer","example":5,"description":"Display order"},"view_asset":{"type":"boolean","example":true,"description":"Whether attribute is visible on the asset"},"dependency":{"type":"string","nullable":true,"example":"{if #153#}1{/if}","description":"Attribute dependencies (AfineoScript)"},"scope":{"type":"string","example":"reference_value","enum":["reference_value","overloadable_repository_value","reference_value_directly_editable_locally","contextual_value","shared_value_to_project_group"],"description":"Attribute scope"},"langs_label":{"type":"object","additionalProperties":{"type":"string"},"example":{"Français (fr)":"Nom du produit","English (en)":"Product name"},"description":"Label translations by language"},"configuration":{"type":"object","description":"Specific configuration based on data_type. Properties vary depending on the attribute type. See JSON schemas in attributes_schema folder for complete property list per data_type.","additionalProperties":true,"example":{"name":"role_231","system":false,"class_linked_id":null,"suffix_text":"€","suffix_list":null,"specifications":null,"alignment":0,"character_limit":"0-255","unique":0,"required":true,"is_crossed_data":false,"default_value":null,"default_value_afs":0,"ai_completion":0,"multilingual":true,"multilingual_default_value":0,"property":0,"property_label":"standard","remain_visible_master_asset":null,"editable":true,"indexed":false,"display":false,"help":"Help text","help_display":"overview","afs":null,"default_inheritance":false,"inheritable":false,"crossed_attributes":[],"permissions":[],"asset_types":[],"langs_help":[],"langs_default_value":[]}}},"required":["id","code","label","tags","class_id","data_type","type","order","view_asset","scope","langs_label","configuration"]}},"required":["status_code","status_message","result"],"description":"Attribute information"},{"type":"object","title":"Error Response","properties":{"status_code":{"type":"integer","example":2,"description":"Status code (2 = not found)"},"status_message":{"type":"string","example":"Attribute \"231\" does not exist","description":"Error message with attribute ID"},"result":{"type":"array","items":{"type":"object"},"example":[],"description":"Empty array on error"}},"required":["status_code","status_message","result"]}]}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"integer","description":"Attribute ID. Use POST /api/v3/attributes/list with class_id to get available attribute IDs for a library.","example":231}},"required":["id"]}}}}}},"/api/v3/attributes/groups/create/":{"post":{"tags":["attributes"],"summary":"Create a new attribute group","description":"## Purpose\n\nCreates a new attribute group within a tab. Groups organize attributes by theme within a tab.\n\n## Required Parameters\n\n- `class_id`: Library where the group will be created\n- `tab_id`: Parent tab ID (must belong to the specified library)\n- `label`: Display name for the group\n\n## Idempotent Behavior\n\nIf a group with the same label already exists in the same tab, the endpoint returns the existing group with `created: false` instead of creating a duplicate.\n\n## Modeling Hierarchy\n\nLibrary → Tab → **Group** → Attributes\n","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Group creation response","content":{"application/json":{"schema":{"oneOf":[{"type":"object","title":"Success Response","properties":{"status_code":{"type":"integer","example":1,"description":"Status code (1 = success)"},"status_message":{"type":"string","example":"Group created successfully","description":"Success message"},"result":{"type":"object","properties":{"id":{"type":"integer","example":78,"description":"Group ID"},"class_id":{"type":"integer","example":4,"description":"Library ID"},"tab_id":{"type":"integer","example":45,"description":"Parent tab ID"},"label":{"type":"string","example":"Dimensions","description":"Group label"},"order":{"type":"integer","example":3,"description":"Display order within tab"},"created":{"type":"boolean","example":true,"description":"true if newly created, false if already existed"}},"required":["id","class_id","tab_id","label","order","created"]}},"required":["status_code","status_message","result"]},{"type":"object","title":"Error Response","properties":{"status_code":{"type":"integer","example":2,"description":"Status code (2 = validation error)"},"status_message":{"type":"string","example":"Tab does not belong to specified library","description":"Error message"},"result":{"type":"array","items":{"type":"object"},"example":[],"description":"Empty array on error"}},"required":["status_code","status_message","result"]}]}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"class_id":{"type":"integer","description":"Library ID. Use POST /api/v3/administration/classes/list to get available libraries.","example":4},"tab_id":{"type":"integer","description":"Parent tab ID. Must belong to the specified library. Use POST /api/v3/attributes/tabs/list with class_id to get available tabs.","example":45},"label":{"type":"string","description":"Group label (display name). Common examples: Dimensions, Pricing, Identification, Properties.","example":"Dimensions"}},"required":["class_id","tab_id","label"]}}}}}},"/api/v3/attributes/groups/list/":{"post":{"tags":["attributes"],"summary":"List attribute groups","description":"## Purpose\n\nReturns attribute groups within tabs for a library. Groups organize attributes within tabs.\n","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Groups list response","content":{"application/json":{"schema":{"oneOf":[{"type":"object","title":"Success Response","properties":{"status_code":{"type":"integer","example":1,"description":"Status code (1 = success)"},"status_message":{"type":"string","example":"","description":"Empty string on success"},"result":{"type":"object","properties":{"results_total":{"type":"integer","example":5,"description":"Total number of groups"},"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"integer","example":81,"description":"Group ID"},"label":{"type":"string","example":"Product Properties","description":"Group label"},"tab_id":{"type":"integer","example":105,"description":"Parent tab ID"},"tab_label":{"type":"string","example":"General","description":"Parent tab label"},"order":{"type":"integer","example":3,"description":"Display order within tab"},"description":{"type":"string","nullable":true,"example":null,"description":"Attribute group description or help text (may be null)"}},"required":["id","label","tab_id","tab_label","order"]},"description":"Array of groups"}},"required":["results_total","items"]}},"required":["status_code","status_message","result"]},{"type":"object","title":"Error Response","properties":{"status_code":{"type":"integer","example":3,"description":"Status code (3 = no groups found)"},"status_message":{"type":"string","example":"No groups found","description":"Error message"},"result":{"type":"array","items":{"type":"object"},"example":[],"description":"Empty array on error"}},"required":["status_code","status_message","result"]}]}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"class_id":{"type":"integer","description":"Library ID. Use POST /api/v3/administration/classes/list to get available libraries.","example":4},"tab_id":{"type":"integer","description":"Filter by tab. Use POST /api/v3/attributes/tabs/list with class_id to get available tabs.","example":105}},"required":["class_id"]}}}}}},"/api/v3/attributes/groups/update/":{"post":{"tags":["attributes"],"summary":"Update an existing attribute group","description":"## Purpose\n\nUpdates properties of an existing attribute group. Only provided fields are modified — omitted fields remain unchanged.\n\n## What can be updated\n\n- `label`: Display name\n\n## What cannot be updated\n\n- `tab_id` (immutable — the group belongs to a specific tab)\n\n## Important\n\nIf no updatable field is provided, the endpoint returns success with an empty `updated_fields` list.\n\n## Modeling Hierarchy\n\nLibrary → Tab → **Group** → Attributes\n","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Group update response","content":{"application/json":{"schema":{"oneOf":[{"type":"object","title":"Success Response","properties":{"status_code":{"type":"integer","example":1,"description":"Status code (1 = success)"},"status_message":{"type":"string","example":"Group updated successfully","description":"Status message"},"result":{"type":"object","properties":{"id":{"type":"integer","example":78,"description":"Group ID"},"tab_id":{"type":"integer","example":45,"description":"Parent tab ID"},"label":{"type":"string","example":"Dimensions","description":"Group label"},"updated_fields":{"type":"array","items":{"type":"string"},"example":["label"],"description":"List of fields that were actually modified"}},"required":["id","tab_id","label","updated_fields"]}},"required":["status_code","status_message","result"]},{"type":"object","title":"Error Response","properties":{"status_code":{"type":"integer","enum":[2,997],"example":2,"description":"Status code (2 = validation error, 997 = insufficient permissions)"},"status_message":{"type":"string","example":"Group not found","description":"Error message"},"result":{"type":"array","items":{"type":"object"},"example":[],"description":"Empty array on error"}},"required":["status_code","status_message","result"]}]}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"integer","description":"Group ID to update. Use POST /api/v3/attributes/groups/list with class_id and tab_id to get available groups.","example":78},"label":{"type":"string","description":"New group label (display name). Common examples: Dimensions, Pricing, Identification, Properties. Maximum 255 characters. Empty strings are not allowed.","example":"Dimensions"}},"required":["id"]}}}}}},"/api/v3/attributes/list/":{"post":{"tags":["attributes"],"summary":"List class attributes (simplified)","description":"## Purpose\n\nReturns essential attribute information (id, code, label, data_type) for a class.\n\n## Pagination\n\nUse `num_page` and `results_per_page` together. Results per page: 10-1000.\n\n## Filtering\n\nFilter by `code`, `label`, `data_type`, `system`, `required`, `multilingual`, `indexed`, `scope`, and more. See params for all options.\n","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Attributes information response","content":{"application/json":{"schema":{"oneOf":[{"type":"object","title":"Success Response","properties":{"status_code":{"type":"integer","example":1,"description":"Status code (1 = success)"},"status_message":{"type":"string","example":"","description":"Empty string on success"},"result":{"type":"object","properties":{"results_total":{"type":"integer","example":5,"description":"Total number of attributes (before pagination)"},"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"integer","example":15,"description":"Attribute ID"},"code":{"type":"string","example":"attribute_code","description":"Attribute code"},"label":{"type":"string","example":"My attribute","description":"Attribute label"},"tags":{"type":"array","items":{"type":"string"},"example":["tag1","tag2"],"description":"Array of tags (can be empty)"},"data_type":{"type":"string","example":"short_text","enum":["afs","asset_link","boolean","characteristic","checkbox","counter","data_table","date","datetime","decimal","dropdown","dropdown_editable","dropdown_items","dropdown_items_editable","dynamic_afs","dynamic_link","ean","email","etim","html","integer","long_text","multi_dropdown","multi_dropdown_items","passthrough","price","product_table","radio","radio_items","rich_text","short_text","splitter","tiles","tiles_required","url"],"description":"Attribute data type (short_text, integer, dropdown_items, etc.)"},"required":{"type":"integer","example":1,"description":"Required attribute (0=no, 1=yes)"},"multilingual":{"type":"integer","example":1,"description":"Multilingual attribute (0=no, 1=yes)"},"indexed":{"type":"integer","example":0,"description":"Indexed attribute (0=no, 1=yes)"},"tab_label":{"type":"string","example":"My tab label","description":"Tab attribute label"},"group_label":{"type":"string","nullable":true,"example":"Product Properties","description":"Attribute group label (null if attribute has no group)"},"description":{"type":"string","nullable":true,"example":"A description","description":"Attribute description text (may be null)"},"scope":{"type":"string","example":"reference_value","enum":["reference_value","overloadable_repository_value","reference_value_directly_editable_locally","contextual_value","shared_value_to_project_group"],"description":"Attribute scope (reference_value, contextual_value, etc.)"}},"required":["id","code","label","tags","data_type","required","multilingual","indexed","tab_label","scope"]},"description":"Array of attributes objects"},"num_page":{"type":"integer","nullable":true,"example":1,"description":"Current page number (only if pagination is used)"},"nb_pages":{"type":"integer","nullable":true,"example":3,"description":"Total number of pages (only if pagination is used)"},"results_per_page":{"type":"integer","nullable":true,"example":50,"description":"Number of results per page (only if pagination is used)"}},"required":["results_total","items"]}},"required":["status_code","status_message","result"]},{"type":"object","title":"Error Response","properties":{"status_code":{"type":"integer","example":2,"description":"Status code (2 = not found)"},"status_message":{"type":"string","example":"Class \"4\" does not exist","description":"Error message with class ID"},"result":{"type":"array","items":{"type":"object"},"example":[],"description":"Empty array on error"}},"required":["status_code","status_message","result"]}]}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"class_id":{"type":"integer","description":"Library ID. Use POST /api/v3/administration/classes/list to get available libraries.","example":4},"num_page":{"type":"integer","description":"Page number. Must be used together with results_per_page.","example":1},"results_per_page":{"type":"integer","description":"Number of results per page (between 10 and 1000). Must be used together with num_page.","example":50},"filters":{"type":"object","description":"Optional filter object with type_id (filter by attribute type) and other criteria.","example":{"data_type":"date","system":false,"indexed":true},"properties":{"code":{"type":"string","description":"Partial match on attribute code (case-insensitive)","example":"product"},"label":{"type":"string","description":"Partial match on attribute label (case-insensitive)","example":"price"},"class_linked_id":{"type":"integer","description":"Associated library ID. Use POST /api/v3/administration/classes/list to get available libraries.","example":22},"description":{"type":"string","description":"Partial match on attribute description","example":"product"},"tab_id":{"type":"integer","description":"Tab ID. Use POST /api/v3/attributes/tabs/list with class_id to get available tabs.","example":105},"tab_label":{"type":"string","description":"Tab label","example":"My tab label"},"group_id":{"type":"integer","description":"Attribute group ID. Use POST /api/v3/attributes/groups/list with class_id to get available groups.","example":81},"group_label":{"type":"string","description":"Group label","example":"My group label"},"name":{"type":"string","description":"Partial match on attribute internal name (e.g. role_231)","example":"role_231"},"system":{"type":"boolean","description":"Filter by system attribute (true = system-managed only, false = user-created only)","example":false},"data_type":{"type":"string","description":"Attribute data type","enum":["afs","asset_link","boolean","characteristic","checkbox","counter","data_table","date","datetime","decimal","dropdown","dropdown_editable","dropdown_items","dropdown_items_editable","dynamic_afs","dynamic_link","ean","email","etim","html","integer","long_text","multi_dropdown","multi_dropdown_items","passthrough","price","product_table","radio","radio_items","rich_text","short_text","splitter","tiles","tiles_required","url"],"example":"short_text"},"list_values":{"type":"integer","description":"Values list ID. Use POST /api/v3/listvalues/list with class_id to get available values lists.","example":32},"list":{"type":"boolean","description":"Filter by asset bin attribute (true = bin/collection attributes only, false = standard attributes only)","example":false},"unique":{"type":"integer","description":"Unique value(s) (0=none, 1=check, 2=block)","example":2},"required":{"type":"boolean","description":"Filter by required attribute (true = required only, false = optional only)","example":true},"asset_link_management":{"type":"boolean","description":"Filter by advanced value attribute (true = advanced_value only, false = simple_value only)","example":false},"is_crossed_data":{"type":"boolean","description":"Filter by advanced link attribute","example":false},"multilingual":{"type":"boolean","description":"Filter by multilingual attribute","example":false},"property":{"type":"integer","description":"Property value(s) (0=Standard, 1=Relevant, 2=Variation)","example":0},"editable":{"type":"boolean","description":"Filter by editable attribute (true = editable only, false = read-only only)","example":true},"indexed":{"type":"boolean","description":"Filter by indexed attribute (true = indexed for search, false = not indexed)","example":false},"view_asset":{"type":"boolean","description":"Filter by visibility on asset sheet (true = visible on asset, false = hidden)","example":true},"display":{"type":"boolean","description":"Filter by display in asset list columns (true = shown in list, false = hidden)","example":false},"scope":{"type":"string","description":"Filter by attribute scope (reference_value, overloadable_repository_value, contextual_value, etc.)","enum":["reference_value","overloadable_repository_value","reference_value_directly_editable_locally","contextual_value","shared_value_to_project_group"],"example":"contextual_value"},"default_inheritance":{"type":"boolean","description":"Filter by default inheritance (true = inherited by child assets by default)","example":true},"inheritable":{"type":"boolean","description":"Filter by inheritable attribute","example":false},"usage":{"type":"boolean","description":"Filter by usage (0=multi-valued, 1=single-valued)","example":true},"create_value":{"type":"boolean","description":"Filter by create value attribute","example":true},"search":{"type":"integer","description":"Search value(s) (0=no, 1=yes in full screen mode, 2=yes in drop-down mode)","example":2}}}},"required":["class_id"]}}}}}},"/api/v3/attributes/masks/":{"post":{"tags":["attributes"],"summary":"Get attribute mask permissions","description":"## Purpose\n\nReturns display mask assignments and permissions for an attribute.\n\n## Response Details\n\nEach assignment includes:\n- `mask_id`, `mask_label`: Display mask identification\n- `hidden`: attribute visibility for this mask\n- `read`, `write`: permission flags\n- `locally_editable`: reference value editability in assetShare\n","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Attribute mask permissions response","content":{"application/json":{"schema":{"oneOf":[{"type":"object","title":"Success Response","properties":{"status_code":{"type":"integer","example":1,"description":"Status code (1 = success)"},"status_message":{"type":"string","example":"","description":"Empty string on success"},"result":{"type":"object","properties":{"results_total":{"type":"integer","example":5,"description":"Total number of display masks assigned to this attribute"},"items":{"type":"array","items":{"type":"object","properties":{"mask_id":{"type":"integer","example":1557,"description":"Display mask ID"},"mask_label":{"type":"string","example":"Standard","description":"Display mask label"},"hidden":{"type":"boolean","example":false,"description":"Whether attribute is hidden for this mask (no LCA entry exists)"},"read":{"type":"boolean","example":true,"description":"Read permission for this attribute with this mask"},"write":{"type":"boolean","example":true,"description":"Write permission for this attribute with this mask"},"locally_editable":{"type":"boolean","example":false,"description":"Whether reference value is directly editable locally (assetShare)"}},"required":["mask_id","mask_label","hidden","read","write","locally_editable"]},"description":"Array of mask permissions for this attribute"}},"required":["results_total","items"]}},"required":["status_code","status_message","result"]},{"type":"object","title":"Error Response","properties":{"status_code":{"type":"integer","example":2,"description":"Status code (2 = attribute not found)"},"status_message":{"type":"string","example":"Attribute \"231\" does not exist","description":"Error message"},"result":{"type":"array","items":{"type":"object"},"example":[],"description":"Empty array on error"}},"required":["status_code","status_message","result"]},{"type":"object","title":"Not Found Response","properties":{"status_code":{"type":"integer","example":3,"description":"Status code (3 = no permissions found)"},"status_message":{"type":"string","example":"No mask permissions found for attribute \"231\"","description":"Error message"},"result":{"type":"array","items":{"type":"object"},"example":[],"description":"Empty array on error"}},"required":["status_code","status_message","result"]}]}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"integer","description":"Attribute ID. Use POST /api/v3/attributes/list with class_id to get available attribute IDs for a library.","example":231}},"required":["id"]}}}}}},"/api/v3/attributes/schema/":{"post":{"tags":["attributes"],"summary":"Get attribute schema by data type","description":"## Purpose\n\nReturns the JSON schema defining all available properties for a specific attribute data type.\n\n## Response Structure\n\nThe schema returns all properties at root level (flat structure).\n\n**Important**: When calling the create endpoint, common properties (`class_id`, `tab_id`, `data_type`, `label`, `code`, `group_id`, `description`) go at root level, while all other properties from the schema should be placed inside the `configuration` object.\n\n**Note**: JSON Schema standard keys are converted to snake_case in the response (`allOf` -> `all_of`, `maxLength` -> `max_length`, `additionalProperties` -> `additional_properties`).\n\n## Use Cases\n\n- Discover required and optional fields before calling POST /api/v3/attributes/create/\n- Understand validation rules, default values, and enum constraints for each property\n\n## Workflow\n\n1. Call this endpoint with the desired data_type\n2. Receive the flat JSON schema listing all available properties with types, defaults, and validation rules\n3. When preparing your create request: put common fields at root level, put all type-specific fields inside `configuration: {...}`\n","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Schema response","content":{"application/json":{"schema":{"oneOf":[{"type":"object","title":"Success Response","properties":{"status_code":{"type":"integer","example":1,"description":"Status code (1 = success)"},"status_message":{"type":"string","example":"","description":"Empty string on success"},"result":{"type":"object","description":"JSON Schema object defining the attribute properties","properties":{"$schema":{"type":"string","example":"http://json-schema.org/draft-07/schema#"},"title":{"type":"string","example":"Schéma de l'attribut Afineo : texte court"},"description":{"type":"string","example":"Schéma décrivant toutes les options possibles pour un attribut de nature : short_text"},"type":{"type":"string","example":"object"},"properties":{"type":"object","description":"Property definitions with types, descriptions, and validation rules","additionalProperties":true},"allOf":{"type":"array","description":"Conditional validation rules based on property values","items":{"type":"object"}}}}},"required":["status_code","status_message","result"]},{"type":"object","title":"Error Response","properties":{"status_code":{"type":"integer","example":2,"description":"Status code (2 = invalid data_type)"},"status_message":{"type":"string","example":"Schema not found for data_type \"invalid_type\"","description":"Error message"},"result":{"type":"array","items":{"type":"object"},"example":[],"description":"Empty array on error"}},"required":["status_code","status_message","result"]}]}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data_type":{"type":"string","description":"The attribute data type to get the creation schema for. Returns all available properties (common + configuration object) with types, descriptions, default values, and validation rules. Use this before calling POST /api/v3/attributes/create/ to understand required and optional fields for the chosen type.","enum":["afs","asset_link","boolean","checkbox","counter","data_table","date","datetime","decimal","dropdown_items","dropdown_items_editable","dynamic_afs","dynamic_link","ean","email","html","integer","long_text","multi_dropdown_items","passthrough","price","product_table","radio_items","rich_text","short_text","splitter","url"],"example":"short_text"}},"required":["data_type"]}}}}}},"/api/v3/attributes/tabs/create/":{"post":{"tags":["attributes"],"summary":"Create a new attribute tab","description":"## Purpose\n\nCreates a new display tab in a library. Tabs organize attributes into logical sections in the asset editing interface.\n\n## Required Parameters\n\n- `class_id`: Library where the tab will be created\n- `label`: Display name for the tab\n\n## Idempotent Behavior\n\nIf a tab with the same label already exists in the library, the endpoint returns the existing tab with `created: false` instead of creating a duplicate.\n\n## Modeling Hierarchy\n\nLibrary → **Tab** → Group → Attributes\n","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Tab creation response","content":{"application/json":{"schema":{"oneOf":[{"type":"object","title":"Success Response","properties":{"status_code":{"type":"integer","example":1,"description":"Status code (1 = success)"},"status_message":{"type":"string","example":"Tab created successfully","description":"Success message"},"result":{"type":"object","properties":{"id":{"type":"integer","example":45,"description":"Tab ID"},"class_id":{"type":"integer","example":4,"description":"Library ID"},"label":{"type":"string","example":"Technical information","description":"Tab label"},"order":{"type":"integer","example":3,"description":"Display order"},"created":{"type":"boolean","example":true,"description":"true if newly created, false if already existed"}},"required":["id","class_id","label","order","created"]}},"required":["status_code","status_message","result"]},{"type":"object","title":"Error Response","properties":{"status_code":{"type":"integer","example":2,"description":"Status code (2 = validation error)"},"status_message":{"type":"string","example":"Label cannot be empty","description":"Error message"},"result":{"type":"array","items":{"type":"object"},"example":[],"description":"Empty array on error"}},"required":["status_code","status_message","result"]}]}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"class_id":{"type":"integer","description":"Library ID. Use POST /api/v3/administration/classes/list to get available libraries.","example":4},"label":{"type":"string","description":"Tab label (display name). Common examples: Identification, Commercial, Technical, Logistics, Marketing.","example":"Technical information"}},"required":["class_id","label"]}}}}}},"/api/v3/attributes/tabs/list/":{"post":{"tags":["attributes"],"summary":"List attribute tabs","description":"## Purpose\n\nReturns attribute tabs (form sections) for a library. Tabs organize attributes into logical groups in the asset editing interface.\n","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Tabs list response","content":{"application/json":{"schema":{"oneOf":[{"type":"object","title":"Success Response","properties":{"status_code":{"type":"integer","example":1,"description":"Status code (1 = success)"},"status_message":{"type":"string","example":"","description":"Empty string on success"},"result":{"type":"object","properties":{"results_total":{"type":"integer","example":5,"description":"Total number of tabs"},"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"integer","example":105,"description":"Tab ID"},"label":{"type":"string","example":"General","description":"Tab label"},"order":{"type":"integer","example":1,"description":"Display order"},"visible":{"type":"boolean","example":true,"description":"Whether the tab is visible"},"description":{"type":"string","nullable":true,"example":null,"description":"Tab description or help text (may be null)"}},"required":["id","label","order","visible"]},"description":"Array of tabs"}},"required":["results_total","items"]}},"required":["status_code","status_message","result"]},{"type":"object","title":"Error Response","properties":{"status_code":{"type":"integer","example":3,"description":"Status code (3 = no tabs found)"},"status_message":{"type":"string","example":"No tabs found","description":"Error message"},"result":{"type":"array","items":{"type":"object"},"example":[],"description":"Empty array on error"}},"required":["status_code","status_message","result"]}]}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"class_id":{"type":"integer","description":"Library ID. Use POST /api/v3/administration/classes/list to get available libraries.","example":4},"include_hidden":{"type":"boolean","description":"Include hidden tabs. By default, only visible tabs are returned.","example":false}},"required":["class_id"]}}}}}},"/api/v3/attributes/tabs/update/":{"post":{"tags":["attributes"],"summary":"Update an existing attribute tab","description":"## Purpose\n\nUpdates properties of an existing display tab. Only provided fields are modified — omitted fields remain unchanged.\n\n## What can be updated\n\n- `label`: Display name\n- `order`: Tab position among the library tabs\n\n## What cannot be updated\n\n- `class_id` (immutable — the tab belongs to a specific library)\n\n## Modeling Hierarchy\n\nLibrary → **Tab** → Group → Attributes\n","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Tab update response","content":{"application/json":{"schema":{"oneOf":[{"type":"object","title":"Success Response","properties":{"status_code":{"type":"integer","example":1,"description":"Status code (1 = success)"},"status_message":{"type":"string","example":"Tab updated successfully","description":"Status message"},"result":{"type":"object","properties":{"id":{"type":"integer","example":45,"description":"Tab ID"},"class_id":{"type":"integer","example":4,"description":"Library ID"},"label":{"type":"string","example":"Technical information","description":"Tab label"},"updated_fields":{"type":"array","items":{"type":"string"},"example":["label"],"description":"List of fields that were actually modified"}},"required":["id","class_id","label","updated_fields"]}},"required":["status_code","status_message","result"]},{"type":"object","title":"Error Response","properties":{"status_code":{"type":"integer","enum":[2,997],"example":2,"description":"Status code (2 = validation error, 997 = insufficient permissions)"},"status_message":{"type":"string","example":"Tab not found","description":"Error message"},"result":{"type":"array","items":{"type":"object"},"example":[],"description":"Empty array on error"}},"required":["status_code","status_message","result"]}]}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"integer","description":"Tab ID to update. Use POST /api/v3/attributes/tabs/list with class_id to get available tabs.","example":45},"label":{"type":"string","description":"New tab label (display name)","example":"Technical information"},"order":{"type":"integer","description":"Tab position (order) among the library tabs.","example":3}},"required":["id"]}}}}}},"/api/v3/attributes/types/":{"post":{"tags":["attributes"],"summary":"Get attribute type assignments","description":"## Purpose\n\nReturns asset type assignments for an attribute.\n\n## Response Details\n\nEach assignment includes:\n- `type_id`, `type_label`: Asset type identification\n- `supertype_id`, `supertype_label`: Parent supertype\n- `editability`: default, editable, or not_editable\n- `hide_interface`: visibility in UI\n","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Attribute type assignments response","content":{"application/json":{"schema":{"oneOf":[{"type":"object","title":"Success Response","properties":{"status_code":{"type":"integer","example":1,"description":"Status code (1 = success)"},"status_message":{"type":"string","example":"","description":"Empty string on success"},"result":{"type":"object","properties":{"results_total":{"type":"integer","example":5,"description":"Total number of asset types assigned to this attribute"},"items":{"type":"array","items":{"type":"object","properties":{"type_id":{"type":"integer","example":407,"description":"Asset type ID"},"type_label":{"type":"string","nullable":true,"example":"Product","description":"Asset type label"},"supertype_id":{"type":"integer","nullable":true,"example":1,"description":"Supertype ID"},"supertype_label":{"type":"string","nullable":true,"example":"Record","description":"Supertype label"},"editability":{"type":"string","enum":["default","editable","not_editable"],"example":"editable","description":"Editability setting for this attribute on this type"},"hide_interface":{"type":"boolean","example":false,"description":"Whether the attribute is hidden in the interface for this type"}},"required":["type_id","type_label","supertype_id","supertype_label","editability","hide_interface"]},"description":"Array of type assignments for this attribute"}},"required":["results_total","items"]}},"required":["status_code","status_message","result"]},{"type":"object","title":"Error Response","properties":{"status_code":{"type":"integer","example":2,"description":"Status code (2 = attribute not found)"},"status_message":{"type":"string","example":"Attribute \"231\" does not exist","description":"Error message"},"result":{"type":"array","items":{"type":"object"},"example":[],"description":"Empty array on error"}},"required":["status_code","status_message","result"]},{"type":"object","title":"Not Found Response","properties":{"status_code":{"type":"integer","example":3,"description":"Status code (3 = no assignments found)"},"status_message":{"type":"string","example":"No type assignments found for attribute \"231\"","description":"Error message"},"result":{"type":"array","items":{"type":"object"},"example":[],"description":"Empty array on error"}},"required":["status_code","status_message","result"]}]}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"integer","description":"Attribute ID. Use POST /api/v3/attributes/list with class_id to get available attribute IDs for a library.","example":231}},"required":["id"]}}}}}},"/api/v3/attributes/update/":{"post":{"tags":["attributes"],"summary":"Update an existing attribute","description":"## Purpose\n\nUpdates an existing attribute's properties. Only send the properties you want to change.\n\n## Immutable Fields (cannot be changed)\n\n- `class_id`: The attribute cannot be moved to a different library\n- `class_linked_id`: The link target library cannot be changed after creation\n- `type`: The simple/advanced value nature is fixed at creation (sending `type` returns an error)\n\n## Modifiable but constrained\n\n- `data_type`: Can be changed **only to a data_type compatible with the current format** (same conversion rules as the edit UI — e.g. text↔date↔price, or select↔radio). Incompatible conversions are rejected. `ROLE_Format` is synchronized automatically. `counter` cannot be converted (and nothing can be converted to `counter`).\n- `code`: Can be renamed, subject to the same format (`^[a-zA-Z][a-zA-Z0-9_]*$`) and global uniqueness validation as at creation.\n- `is_crossed_data` (in `configuration`): Can be toggled (like the edit UI), **only if**: the library accepts crossed data (`isAdvancedLink`), the attribute is an advanced data attribute (`mod_datas`), and the data_type is not one of afs/dynamic_afs/splitter/etim/characteristic. ⚠️ Turning it **off** (1→0) triggers `cleanRoleAdvancedSlave()` which **deletes the linked (slave) attributes** — destructive, same as the UI.\n\n## Workflow\n\n1. First call POST /api/v3/attributes/get/ with the parameter `id` to see current values\n2. Prepare only the properties you want to change\n3. Call this endpoint with parameter `id` and the changed properties\n\n## Parameter Structure\n\nParameters are organized in two levels:\n- **Root level**: `id` (required) + common properties (label, tab_id, group_id, description)\n- **configuration**: Object containing type-specific properties to update (same properties as create, all optional)\n\n## What Can Be Updated\n\n### Root Level Properties\n- `label`: Change the display label\n- `tab_id`: Move attribute to a different tab (must belong to same library)\n- `group_id`: Move attribute to a different group (must belong to the target tab). Set to 0 to remove from group.\n- `description`: Change the description\n\n### Configuration Properties (type-specific)\nAll configuration properties of the attribute's data_type can be updated — send them inside `configuration` (all optional). The available properties and their rules are **identical to create** (see the shared rules below). `class_linked_id` is **immutable** (see above). Use POST /api/v3/attributes/schema/ for the exhaustive per-data_type property list.\n\n## Default Value: static vs AFS-computed (mutually exclusive)\n\nAn attribute can have **at most one** default value, in **one of two modes** — never both:\n\n| Mode | How to set it | Behavior |\n|---|---|---|\n| **Static** default | `default_value` = \"<fixed text>\" (and `default_value_afs` = 0 or omitted) | The fixed value is prefilled on new records. |\n| **AFS-computed** default | `default_value_afs` = 1 **and** `afs` = \"<AfineoScript>\" (do **not** send `default_value`) | When a record has no value, the `afs` script is evaluated to produce the default (\"computed default when empty\"). |\n\n- These modes are **mutually exclusive**: sending `default_value` together with `default_value_afs=1` is **rejected** (it would be ambiguous). Choose one.\n- **To switch AFS-computed → static**: send `default_value_afs=0` **with** `default_value`. Sending `default_value` alone does not clear an existing AFS default.\n- ⚠️ Do not confuse this scalar `afs` (a *default value*) with the `afs` / `dynamic_afs` **data types**, where `afs` is its **core formula** (always computed, not a fallback default).\n\n## Type-Specific Restrictions\n\nSome data types have restrictions on configuration properties:\n- `splitter`: indexed and editable are forbidden (visual separator only)\n- `counter`: editable is forbidden (auto-incremented)\n- `multi_dropdown_items`: multilingual is forbidden (managed at list level)\n- `passthrough`: required, unique, indexed, default_value, character_limit are forbidden (display-only)\n- `inheritable` and `default_inheritance` are mutually exclusive (cannot both be true)\n- `ai_completion` is only available for short_text, long_text, and rich_text\n- `create_value` and `display_assets` are only available for asset_link and dynamic_link\n- `asset_multiple` requires `display_assets` to be 2\n- `display_assets` = 0 forbids `display_assets_groupby` and `display_assets_attributes` (asset_link, dynamic_link)\n- `display_assets_attributes` (table mode): **ordered array of attribute IDs of the linked library** to show as columns (order = column order), e.g. `[732, 737]`. `display_assets_groupby`: attribute ID to group rows by. Both require `display_assets` >= 1. Sending an empty `display_assets_attributes` is a no-op (columns unchanged).\n- `search` = 0 (search disabled) forbids `search_context`, `search_advanced`, `search_afs`, `search_advanced_freeze`, `dynamic_search_context`; `search` = 2 forbids `search_advanced` (asset_link, dynamic_link)\n- `remain_visible_master_asset` requires `property` to be 2\n- `multilingual_default_value` requires `multilingual` to be 1\n- When `default_value_afs` is 1, `afs` (AfineoScript formula) is required\n- `default_value` (static default) and `default_value_afs`=1 (AFS-computed default) are **mutually exclusive** — sending both is rejected (see \"Default Value\" section above)\n- `afs` is only allowed when `default_value_afs` is 1 for data types other than `afs`/`dynamic_afs` (where `afs` is the core formula)\n- For `type` = `simple_value`: `multilingual`, `multilingual_default_value`, `inheritable`, `default_inheritance`, `ai_completion` are forbidden, and `scope_id` must be 1 (scope is only configurable for advanced values)\n- `scope_id` values: 1 (reference), 2 (overloadable), 3 (locally editable), 4 (contextual), 5 (workspace). **Scope 5 requires the `ATTRIBUT_WORKSPACE` extension** (without it, only 1-4 are allowed).\n- `scope_id` change (update only): cannot be changed on a **reciprocal** attribute. Otherwise, \"free\" transitions without cleaning are 1->2/3 and 3->1. Any other change is allowed **only if the attribute has no values in records**; if records already hold values, they must be cleaned first (via the edit UI) before switching to that scope.\n\n> Note: conditional rules declared in each data_type JSON schema (`allOf`) are enforced automatically at create/update, in addition to the rules above.\n\n## Crossed Data Attributes\n\nSet `is_crossed_data: true` in configuration to make the attribute a crossed data attribute.\n\n**Requirements**:\n- The library must support crossed data\n- Forbidden data_types: afs, dynamic_afs, splitter, etim, characteristic\n\n**Forbidden properties** (automatically disabled when is_crossed_data is true):\nrequired, unique, default_value, default_value_afs, multilingual, property, inheritable, default_inheritance, alignment, character_limit, help, ai_completion, create_value, search, search_context, dependency, dynamic_afs, display_assets, usage, counter_start, counter_afs.\n\n## Restrictions (update-specific)\n\n- System attributes (`system = true`) cannot be updated\n- All the shared rules above apply identically to update. The only update-specific constraints (immutable fields, `data_type` change to a compatible type, `code` rename, `scope_id` transitions, `is_crossed_data` toggle) are described in \"Immutable Fields\" and \"Modifiable but constrained\" above.\n\n## Example: Update label and make required\n\n```json\n{\n  \"id\": 456,\n  \"label\": \"New Product Name\",\n  \"configuration\": {\n    \"required\": 1\n  }\n}\n```\n\n## Example: Move attribute to different tab and group\n\n```json\n{\n  \"id\": 456,\n  \"tab_id\": 110,\n  \"group_id\": 85\n}\n```\n\n## Common Errors\n\n- `Attribute not found: X` - the specified id does not exist\n- `System attributes cannot be updated` - attribute has system flag\n- `Tab not found: X` - the specified tab_id does not exist\n- `Tab X does not belong to class Y` - tab must belong to the attribute's library\n- `Group not found: X` - the specified group_id does not exist\n- `Group X does not belong to tab Y` - group must belong to the target tab\n- `Values list not found: X` - the specified list_values does not exist\n- `Values list X does not belong to class Y` - list must belong to the attribute's library\n- `class_id / class_linked_id / type cannot be modified after creation` - immutable fields (also rejected if sent inside `configuration`)\n- `data_type cannot be changed from \"X\" to \"Y\" (incompatible). Compatible types: ...` - data_type can only change to a type compatible with the current format\n- `is_crossed_data can only be changed on advanced data attributes` - the attribute must be an advanced data attribute (mod_datas) to toggle crossed data\n- `scope_id change from X to Y is locked: N record(s) have a value...` - scope transition beyond 1->2/3 or 3->1 requires the attribute to have no values (clean them first)\n- All validation errors from create also apply (type restrictions, dependency constraints, etc.)\n","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Update attribute response","content":{"application/json":{"schema":{"oneOf":[{"type":"object","title":"Success Response","properties":{"status_code":{"type":"integer","example":1,"description":"Status code (1 = success)"},"status_message":{"type":"string","example":"Attribute updated successfully","description":"Success message"},"result":{"type":"object","description":"Full attribute data after update (same format as GET endpoint)","properties":{"id":{"type":"integer","example":456,"description":"Attribute ID"},"code":{"type":"string","example":"product_name","description":"Attribute code"},"label":{"type":"string","example":"New Product Name","description":"Updated attribute label"},"data_type":{"type":"string","example":"short_text","description":"Attribute data type"},"class_id":{"type":"integer","example":4,"description":"Library ID"},"configuration":{"type":"object","description":"Full type-specific configuration after update"}}}},"required":["status_code","status_message","result"]},{"type":"object","title":"Validation Error Response","properties":{"status_code":{"type":"integer","example":2,"description":"Status code (2 = validation error)"},"status_message":{"type":"string","example":"Attribute not found: 999","description":"Validation error message"},"result":{"type":"array","items":{"type":"object"},"example":[],"description":"Empty array on error"}},"required":["status_code","status_message","result"]},{"type":"object","title":"Error Response","properties":{"status_code":{"type":"integer","example":3,"description":"Status code (3 = update error)"},"status_message":{"type":"string","example":"Error updating attribute: ...","description":"Error message"},"result":{"type":"array","items":{"type":"object"},"example":[],"description":"Empty array on error"}},"required":["status_code","status_message","result"]}]}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"integer","description":"ID of the attribute to update. Use POST /api/v3/attributes/list/ or POST /api/v3/attributes/get/ to find attribute IDs.","example":456},"label":{"type":"string","description":"New display label for the attribute. Maximum 255 characters.","example":"New Product Name"},"tab_id":{"type":"integer","description":"Move attribute to this tab. Must belong to the same library as the attribute. Use POST /api/v3/attributes/tabs/list/ with class_id to get available tabs.","example":110},"group_id":{"type":"integer","description":"Move attribute to this group. Must belong to the target tab (current or new tab_id). Set to 0 to remove from any group. Use POST /api/v3/attributes/groups/list/ with class_id and tab_id to get available groups.","example":85},"description":{"type":"string","description":"New description for the attribute. Maximum 500 characters.","example":"Updated product description field"},"configuration":{"type":"object","description":"Type-specific configuration properties to update. Only include properties you want to change. Available properties depend on the attribute's data_type. Use POST /api/v3/attributes/schema/ to see available properties for a data_type.","example":{"required":1,"multilingual":1,"character_limit":"0-255"}}},"required":["id"]}}}}}}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"JWT token generated with /api/v3/token/get/"}}}}