# Anúncios · Atributos e variações

Os atributos são o vocabulário do anúncio: dizem **o que o produto é** (marca, material, potência) e **no que cada variação difere** (cor, tamanho, voltagem). Quem dita as regras é a [categoria](/guia/anuncios-categorias) — cada uma define seus próprios atributos, obrigatórios ou não.

---

## O vínculo categoria–atributo

<a class="api-route" href="/reference/anuncios#tag/publicacao/GET/api/categories/{category_id}/attributes"><span class="api-method api-method-get">GET</span><span class="api-path">/api/categories/{id}/attributes</span></a>

Cada atributo retornado carrega flags **do vínculo com aquela categoria**:

| Flag | Significado |
|------|-------------|
| `is_required` | Obrigatório — sem ele o anúncio não passa no checklist de publicação. |
| `is_feature` | Característica descritiva do produto (marca, material, peso). |
| `is_variant` | Atributo de variação (cor, tamanho, voltagem). |
| `is_combinable` | Pode entrar na geração de combinações de variações. |
| `is_filter` | Vira filtro de busca na vitrine. |

| Parâmetro | Para quê |
|-----------|----------|
| `matrix=true` | Agrupa por seção (Identificação, Técnico, ...). Sem ele, lista plana. |
| `only_features=1` | Apenas características. |
| `only_variations=1` | Apenas atributos de variação. |

> `only_features` e `only_variations` juntos retornam **422**.

---

## Anatomia de um atributo

A definição é autodescritiva — o backend te diz como montar o formulário e o payload:

```jsonc
{
  "id": 14,
  "name": "Cor",
  "type": "color",                  // estrutura do valor (tabela abaixo)
  "value_type": "select",           // tipo primitivo, relevante nos types default*
  "is_feature": false,
  "is_variant": true,
  "ui": {
    "public_name": "Cor",
    "description": "Cor predominante do produto",
    "select_type": "select"         // select/radio/checkbox = opção pré-cadastrada; text = digitação livre
  },
  "value_definition": {
    "fields": [ /* schema dinâmico do formulário (ex.: value, main_color, hex) */ ],
    "options": [                    // opções pré-cadastradas, quando houver
      {
        "id": 87,
        "value": "Azul",
        "component": { "value": "Azul", "main_color": "blue", "hex": "#1565C0", "brightness": "dark" }
      }
    ]
  }
}
```

**`type`** define a estrutura do valor:

| `type` | O que é | Payload típico |
|--------|---------|----------------|
| `default` | Valor simples | `{ "id": 1, "value": "Algodão" }` |
| `default_unit` | Número + unidade (entre `available_units`) | `{ "id": 5, "value": 350, "unit": "ml" }` |
| `default_list` | Lista de itens | `{ "id": 9, "items": ["Cabo USB", "Manual"] }` |
| `dimension_2d` | Largura × altura + unidade | `{ "id": 11, "width": 20, "height": 30, "unit": "cm" }` |
| `dimension_3d` | Largura × altura × profundidade + unidade | `{ "id": 12, "width": 20, "height": 30, "depth": 10, "unit": "cm" }` |
| `color` | Cor (pré-cadastrada ou customizada) | ver abaixo |
| `brand` | Marca | `{ "id": 2, "value_id": 3 }` ou `{ "id": 2, "value": "HyperX" }` |
| `image`, `packaging` | Tipos especializados | conforme `value_definition.fields` |

**`value_type`** (`text`, `float`, `number`, `date`, `boolean`, `select`, `radio`, `checkbox`) é o tipo primitivo do valor — relevante nos types `default*`; os tipos especializados o ignoram.

**Opção pré-cadastrada vs. valor livre:** quando `ui.select_type` é `select`/`radio`/`checkbox`, envie o `value_id` de uma das `options` (ou um `value` que case exatamente com uma opção). Quando é `text`, digite livre. A exceção é a cor:

### Cor customizada

Atributos `type=color` aceitam, além das opções pré-cadastradas, uma **cor livre**:

```jsonc
{ "id": 14, "value": "Azul Petróleo", "hex": "#0F4C5C" }
```

- `value` (nome) + `hex` (`#RGB` ou `#RRGGBB`) são suficientes.
- `main_color` e `brightness` são opcionais — `brightness` (light/dark) é calculada a partir do hex quando omitida.
- Vale tanto em `attributes[]` quanto em `variations[].attributes[]`; a cor volta completa nas respostas (campo `component`).

---

## Onde os atributos entram no anúncio

No [create/simulate](/guia/anuncios#etapa-5-criar-anuncio), há dois lugares:

- **`attributes[]`** — características do anúncio como um todo (marca, material...).
- **`variations[].attributes[]`** — o que diferencia **cada variação** (a cor azul desta, a voltagem 110 V daquela).

A API aceita os aliases `attribute_id`/`id`, `attribute_value`/`value` e `value_unit`/`unit` — use o par que preferir, mas seja consistente.

---

## Variações: da combinação ao SKU

### 1. Gerar combinações

<a class="api-route" href="/reference/anuncios#tag/publicacao/POST/api/categories/{category_id}/combine-attributes"><span class="api-method api-method-post">POST</span><span class="api-path">/api/categories/{id}/combine-attributes</span></a>

Envie os valores escolhidos de cada atributo `is_combinable` — texto, `option_id` ou objeto (cor customizada, dimensões):

```json
{
  "attributes": {
    "1": ["110 V", "220 V"],
    "14": [{ "value": "Azul Petróleo", "hex": "#0F4C5C", "custom": true }, "Verde"]
  },
  "primary_attribute_id": 1
}
```

A resposta traz `combinations[]` (o produto cartesiano, cada uma com `attributes`, `attributes_details` e `description`) e `primary_attribute`. O atributo principal serve pra **agrupar as fotos por variação** — escolha aquele que muda a aparência (cor, em geral).

> Valores customizados só passam em atributos com `select_type: "text"` ou `type: "color"`; nos demais, use opções pré-cadastradas. Atributo fora da lista de combináveis da categoria → **400**.

### 2. Vincular um SKU em cada variação

Cada combinação vira um item de `variations[]` no create, com seu `seller_sku`:

```json
{
  "seller_sku": "FK-JBL-2000-AZUL",
  "attributes": [{ "attribute_id": 14, "value": "Azul Petróleo", "hex": "#0F4C5C" }],
  "images": [{ "id": "{{image_id}}" }]
}
```

Use **SKUs diferentes** quando cada variação tem estoque/preço próprio, ou o **mesmo SKU** quando as variações são apenas visuais. O SKU precisa existir na loja com preço e estoque configurados.

### Status da variação

`active`, `pending` (default na criação) ou `hidden`. Não confunda com o status da **oferta** de catálogo (`active`, `inactive`, `paused`, `pending`), usado no [attach-sku](/guia/anuncios#fluxo-2-ofertar-em-anuncio-de-catalogo).

---

## Uma pegadinha: atributos calculáveis

Alguns atributos (ex.: Quantidade) são `is_calculable: true`. Em ofertas de catálogo, a **unidade** desse atributo precisa casar com a `base_unit` do seu SKU — divergência retorna **422** (`UNIT_MISMATCH`). Se o anúncio vende "caixa com 12", seu SKU precisa estar cadastrado na unidade compatível.
