> ## Documentation Index
> Fetch the complete documentation index at: https://documentation.oryxa.in/llms.txt
> Use this file to discover all available pages before exploring further.

# Custom Attributes and Product Templates

> Define typed attributes, group them into logical sets, and bundle them into reusable product templates to enforce consistent metadata across your catalog.

Attributes are the building blocks of structured product metadata in Synq. An attribute defines a named, typed property — such as `Material` (type `TEXT`) or `Weight` (type `NUMBER`) — that can be attached to products and variants. You group related attributes into **attribute groups** to keep your data model organized (for example, putting `Material`, `Care Instructions`, and `Country of Origin` into a `Fabric Details` group). Finally, you bundle attribute groups into **product templates** (also called product types), which act as reusable schemas: apply the "Apparel" template to all clothing products to ensure they always carry the same set of defined attributes. Media — product and variant images — is also managed through the PIM API and is covered at the end of this guide.

<Note>
  All requests require the standard headers:
  `Authorization: Bearer <token>`, `X-Tenant-ID`, and `X-Org-ID`.
</Note>

***

## Attributes

### List attributes

Retrieve all active attributes for your tenant, ordered alphabetically by name.

```bash theme={null}
GET /api/v1/pim/attributes
```

```bash theme={null}
curl https://api.synq.com/api/v1/pim/attributes \
  -H "Authorization: Bearer $TOKEN" \
  -H "X-Tenant-ID: $TENANT_ID" \
  -H "X-Org-ID: $ORG_ID"
```

**Response** `200 OK`

```json theme={null}
[
  {
    "id": "a0b1c2d3-0001-4e5f-6a7b-8c9d0e1f2a3b",
    "org_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "tenant_id": "f9e8d7c6-b5a4-3210-fedc-ba9876543210",
    "name": "Material",
    "slug": "material",
    "type": "TEXT",
    "created_at": "2024-10-01T09:00:00Z",
    "updated_at": "2024-10-01T09:00:00Z"
  },
  {
    "id": "a0b1c2d3-0002-4e5f-6a7b-8c9d0e1f2a3b",
    "org_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "tenant_id": "f9e8d7c6-b5a4-3210-fedc-ba9876543210",
    "name": "Weight (grams)",
    "slug": "weight-grams",
    "type": "NUMBER",
    "created_at": "2024-10-01T09:01:00Z",
    "updated_at": "2024-10-01T09:01:00Z"
  }
]
```

***

### Create an attribute

```bash theme={null}
POST /api/v1/pim/attributes
```

**Request body**

<ParamField body="name" type="string" required>
  Human-readable label for the attribute (e.g., `"Material"`).
</ParamField>

<ParamField body="slug" type="string" required>
  URL-safe unique identifier used to reference this attribute programmatically (e.g., `"material"`).
</ParamField>

<ParamField body="type" type="string">
  The data type of attribute values. Defaults to `TEXT` if omitted. Common values: `TEXT`, `NUMBER`, `BOOLEAN`, `DATE`.
</ParamField>

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://api.synq.com/api/v1/pim/attributes \
    -H "Authorization: Bearer $TOKEN" \
    -H "X-Tenant-ID: $TENANT_ID" \
    -H "X-Org-ID: $ORG_ID" \
    -H "Content-Type: application/json" \
    -d '{
      "name": "Material",
      "slug": "material",
      "type": "TEXT"
    }'
  ```

  ```json Request body theme={null}
  {
    "name": "Material",
    "slug": "material",
    "type": "TEXT"
  }
  ```
</CodeGroup>

**Response** `200 OK`

```json theme={null}
{
  "id": "a0b1c2d3-0001-4e5f-6a7b-8c9d0e1f2a3b",
  "org_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "tenant_id": "f9e8d7c6-b5a4-3210-fedc-ba9876543210",
  "name": "Material",
  "slug": "material",
  "type": "TEXT",
  "created_at": "2024-11-15T13:00:00Z",
  "updated_at": "2024-11-15T13:00:00Z"
}
```

<ResponseField name="id" type="string">
  UUID for the new attribute. Use this when linking the attribute to an attribute group or product template.
</ResponseField>

***

## Attribute groups

Attribute groups let you organize related attributes under a shared label. For example, a `"Dimensions"` group might contain `Height`, `Width`, `Depth`, and `Weight`.

### List attribute groups

```bash theme={null}
GET /api/v1/pim/attribute-groups
```

```bash theme={null}
curl https://api.synq.com/api/v1/pim/attribute-groups \
  -H "Authorization: Bearer $TOKEN" \
  -H "X-Tenant-ID: $TENANT_ID" \
  -H "X-Org-ID: $ORG_ID"
```

**Response** `200 OK`

```json theme={null}
[
  {
    "id": "g0b1c2d3-0001-4e5f-6a7b-8c9d0e1f2a3b",
    "org_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "tenant_id": "f9e8d7c6-b5a4-3210-fedc-ba9876543210",
    "name": "Fabric Details",
    "description": "Textile composition and care information.",
    "created_at": "2024-10-01T09:00:00Z",
    "updated_at": "2024-10-01T09:00:00Z"
  }
]
```

***

### Create an attribute group

```bash theme={null}
POST /api/v1/pim/attribute-groups
```

**Request body**

<ParamField body="name" type="string" required>
  Display name for the group (e.g., `"Fabric Details"`).
</ParamField>

<ParamField body="description" type="string">
  An optional description of what this group covers.
</ParamField>

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://api.synq.com/api/v1/pim/attribute-groups \
    -H "Authorization: Bearer $TOKEN" \
    -H "X-Tenant-ID: $TENANT_ID" \
    -H "X-Org-ID: $ORG_ID" \
    -H "Content-Type: application/json" \
    -d '{
      "name": "Fabric Details",
      "description": "Textile composition and care information."
    }'
  ```

  ```json Request body theme={null}
  {
    "name": "Fabric Details",
    "description": "Textile composition and care information."
  }
  ```
</CodeGroup>

**Response** `200 OK`

```json theme={null}
{
  "id": "g0b1c2d3-0001-4e5f-6a7b-8c9d0e1f2a3b",
  "org_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "tenant_id": "f9e8d7c6-b5a4-3210-fedc-ba9876543210",
  "name": "Fabric Details",
  "description": "Textile composition and care information.",
  "created_at": "2024-11-15T13:10:00Z",
  "updated_at": "2024-11-15T13:10:00Z"
}
```

***

## Product templates

A product template (called a product type in the API) is a named, reusable schema that bundles a set of attributes. When you assign a template to a product via `product_type_id`, your team knows exactly which attribute fields are expected to be filled in for that product type.

### List templates

```bash theme={null}
GET /api/v1/pim/templates
```

```bash theme={null}
curl https://api.synq.com/api/v1/pim/templates \
  -H "Authorization: Bearer $TOKEN" \
  -H "X-Tenant-ID: $TENANT_ID" \
  -H "X-Org-ID: $ORG_ID"
```

**Response** `200 OK`

```json theme={null}
[
  {
    "id": "t0b1c2d3-0001-4e5f-6a7b-8c9d0e1f2a3b",
    "org_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "tenant_id": "f9e8d7c6-b5a4-3210-fedc-ba9876543210",
    "name": "Apparel",
    "description": "Standard template for all clothing products.",
    "created_at": "2024-10-01T09:00:00Z",
    "updated_at": "2024-10-01T09:00:00Z"
  }
]
```

***

### Create a template

```bash theme={null}
POST /api/v1/pim/templates
```

**Request body**

<ParamField body="name" type="string" required>
  Display name for the template (e.g., `"Apparel"`).
</ParamField>

<ParamField body="description" type="string">
  An optional description of the product category this template covers.
</ParamField>

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://api.synq.com/api/v1/pim/templates \
    -H "Authorization: Bearer $TOKEN" \
    -H "X-Tenant-ID: $TENANT_ID" \
    -H "X-Org-ID: $ORG_ID" \
    -H "Content-Type: application/json" \
    -d '{
      "name": "Apparel",
      "description": "Standard template for all clothing products."
    }'
  ```

  ```json Request body theme={null}
  {
    "name": "Apparel",
    "description": "Standard template for all clothing products."
  }
  ```
</CodeGroup>

**Response** `200 OK`

```json theme={null}
{
  "id": "t0b1c2d3-0001-4e5f-6a7b-8c9d0e1f2a3b",
  "org_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "tenant_id": "f9e8d7c6-b5a4-3210-fedc-ba9876543210",
  "name": "Apparel",
  "description": "Standard template for all clothing products.",
  "created_at": "2024-11-15T13:20:00Z",
  "updated_at": "2024-11-15T13:20:00Z"
}
```

<ResponseField name="id" type="string">
  UUID for the template. Pass this as `product_type_id` when creating or updating a product to apply this template.
</ResponseField>

***

## Media

Product and variant images are managed through the media endpoints. Each media record links a URL to either a product or a specific variant, with optional alt text and a sort order for controlling display sequence.

### List media

Retrieve media records for your tenant. Pass `product_id` or `variant_id` as query parameters to filter results to a specific product or variant.

```bash theme={null}
GET /api/v1/pim/media
```

```bash theme={null}
curl "https://api.synq.com/api/v1/pim/media?product_id=018e7c3a-1f2b-7d4e-9a0c-3f5b6e7d8c90" \
  -H "Authorization: Bearer $TOKEN" \
  -H "X-Tenant-ID: $TENANT_ID" \
  -H "X-Org-ID: $ORG_ID"
```

**Response** `200 OK`

```json theme={null}
[
  {
    "id": "m0b1c2d3-0001-4e5f-6a7b-8c9d0e1f2a3b",
    "org_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "tenant_id": "f9e8d7c6-b5a4-3210-fedc-ba9876543210",
    "product_id": "018e7c3a-1f2b-7d4e-9a0c-3f5b6e7d8c90",
    "variant_id": null,
    "url": "https://cdn.example.com/products/merino-sweater-hero.jpg",
    "alt_text": "Merino wool crew neck sweater in navy",
    "sort_order": 1,
    "created_at": "2024-11-15T13:30:00Z",
    "updated_at": "2024-11-15T13:30:00Z"
  }
]
```

***

### Create media

```bash theme={null}
POST /api/v1/pim/media
```

**Request body**

<ParamField body="product_id" type="string">
  UUID of the product this image belongs to. Provide either `product_id` or `variant_id`.
</ParamField>

<ParamField body="variant_id" type="string">
  UUID of the variant this image belongs to. Provide either `product_id` or `variant_id`.
</ParamField>

<ParamField body="url" type="string" required>
  A fully qualified URL to the image file.
</ParamField>

<ParamField body="alt_text" type="string">
  Descriptive alt text for the image, used for accessibility and SEO.
</ParamField>

<ParamField body="sort_order" type="integer">
  Integer position controlling the display order of images. Lower numbers appear first.
</ParamField>

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://api.synq.com/api/v1/pim/media \
    -H "Authorization: Bearer $TOKEN" \
    -H "X-Tenant-ID: $TENANT_ID" \
    -H "X-Org-ID: $ORG_ID" \
    -H "Content-Type: application/json" \
    -d '{
      "product_id": "018e7c3a-1f2b-7d4e-9a0c-3f5b6e7d8c90",
      "url": "https://cdn.example.com/products/merino-sweater-hero.jpg",
      "alt_text": "Merino wool crew neck sweater in navy",
      "sort_order": 1
    }'
  ```

  ```json Request body theme={null}
  {
    "product_id": "018e7c3a-1f2b-7d4e-9a0c-3f5b6e7d8c90",
    "url": "https://cdn.example.com/products/merino-sweater-hero.jpg",
    "alt_text": "Merino wool crew neck sweater in navy",
    "sort_order": 1
  }
  ```
</CodeGroup>

**Response** `200 OK`

```json theme={null}
{
  "id": "m0b1c2d3-0001-4e5f-6a7b-8c9d0e1f2a3b",
  "org_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "tenant_id": "f9e8d7c6-b5a4-3210-fedc-ba9876543210",
  "product_id": "018e7c3a-1f2b-7d4e-9a0c-3f5b6e7d8c90",
  "variant_id": null,
  "url": "https://cdn.example.com/products/merino-sweater-hero.jpg",
  "alt_text": "Merino wool crew neck sweater in navy",
  "sort_order": 1,
  "created_at": "2024-11-15T13:30:00Z",
  "updated_at": "2024-11-15T13:30:00Z"
}
```

***

## Putting it together

Here is the recommended sequence for setting up a fully attributed product type:

<Steps>
  <Step title="Create your attributes">
    Define each attribute your product type needs. For an "Apparel" template you might create `Material` (TEXT), `Care Instructions` (TEXT), and `Weight` (NUMBER).
  </Step>

  <Step title="Create an attribute group">
    Group related attributes under a meaningful label, such as `"Fabric Details"`.
  </Step>

  <Step title="Create the product template">
    Create a `POST /api/v1/pim/templates` record named `"Apparel"`.
  </Step>

  <Step title="Link attributes to the template">
    Associate each attribute with the template by providing the template's UUID as `product_type_id` when creating products. When you create a product and set `product_type_id` to this template's UUID, it signals which attributes should be populated for that product.
  </Step>

  <Step title="Apply the template to products">
    When creating or updating products, set `product_type_id` to the template's UUID. This connects the product to the expected attribute schema.
  </Step>
</Steps>
