openapi: 3.1.0
info:
  title: API de Preços da Força de Vendas
  description: Gerencie o cadastro das tabelas de preço e os preços por SKU usados nas vendas da força de vendas. Vendedores e relatórios do Portal ainda não fazem parte deste contrato de integração.
  version: 1.0.0
  contact:
    name: UniSupri
servers:
  - url: https://api.unisupri.com
    description: Produção
  - url: https://api.sandbox.samdevel.com.br
    description: Sandbox (ambiente de homologação)
tags:
  - name: Tabelas de Preço (Força de Vendas)
    description: |-
      CRUD das **tabelas de preço** da loja e o toggle que liga/desliga o sistema. Recurso exclusivo de lojas com **força de vendas** habilitada.
      
      **Como funciona:**
      - Cada tabela tem **nome**, **modo** (`manual` ou `auto`), **posição** e flag **is_active**.
      - O toggle `price_lists_enabled` (em `/settings`) é independente da liberação da força de vendas na conta: mesmo com a feature liberada, o lojista precisa ligar o sistema de tabelas para criar/usar preços por tabela.
      - Apagar uma tabela remove as faixas dos produtos nela (cascata).
      
      **Pré-requisitos / erros de gate:**
      - Loja sem a feature força de vendas → **403** com `data.reason = "feature_disabled"`.
      - Depois de criar as tabelas, use **Preços por Tabela (Força de Vendas)** para definir os preços de cada produto.
  - name: Preços por Tabela (Força de Vendas)
    description: |-
      Preços de um produto nas **tabelas de preço** da loja, recurso exclusivo de lojas com a funcionalidade **força de vendas** habilitada.
      
      **Como funciona:**
      - As tabelas em si (nome, modo, ordem, ativação) são gerenciadas em **Tabelas de Preço (Força de Vendas)** (`/api/seller/price-lists`). Aqui você **consulta e atualiza os preços de um produto** em cada tabela já existente.
      - As tabelas são **totalmente isoladas do preço marketplace** (`/api/products/{id}/prices`): atualizar preços de tabela não altera as faixas de atacado do produto, e vice-versa. São dois catálogos de preço que convivem.
      - Cada tabela tem um **modo**: `manual` (o preço de cada faixa é informado) ou `auto` (o preço deriva de `base_price × (1 − discount_percent/100)`, arredondado a 2 casas; a faixa base nunca tem desconto e os descontos precisam ser progressivos).
      - Até **5 faixas** por produto em cada tabela.
      
      **Pré-requisitos / erros de gate:**
      - Loja sem a feature força de vendas → **403** com `data.reason = "feature_disabled"` em qualquer rota desta seção.
      - Sistema de tabelas desligado nas configurações da loja (`price_lists_enabled = false`) → o **PUT** responde **400** ("Sistema de tabela de preços desativado para esta loja."); a consulta continua funcionando.
paths:
  "/api/seller/price-lists":
    get:
      tags:
        - Tabelas de Preço (Força de Vendas)
      summary: Listar Tabelas de Preço
      description: Retorna todas as tabelas de preço da loja, ordenadas por `position`.
      responses:
        "200":
          description: Lista de tabelas da loja
          content:
            "application/json":
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  message_code:
                    type: string
                  data:
                    type: array
                    items:
                      "$ref": "#/components/schemas/PriceList"
              example:
                success: true
                message_code: SUCCESS
                data:
                  - id: 01JZ4W8H2N4XKQ4R9T4YV9AB1C
                    name: Ouro
                    mode: auto
                    position: 0
                    is_active: true
                  - id: 01JZ4W8H2N4XKQ4R9T4YV9AB2D
                    name: Prata
                    mode: manual
                    position: 1
                    is_active: true
        "401":
          "$ref": "#/components/responses/Unauthorized"
        "403":
          description: Loja sem a funcionalidade força de vendas (ou sem permissão de escopo)
          content:
            "application/json":
              example:
                success: false
                code: 403
                message_code: FORBIDDEN
                description: Força de vendas não habilitada para esta loja.
                data:
                  reason: feature_disabled
                errors: []
                meta: {}
      security:
        - integrationToken: []
    post:
      tags:
        - Tabelas de Preço (Força de Vendas)
      summary: Criar Tabela de Preço
      description: Cria uma nova tabela de preço vazia. O **modo** (`manual` ou `auto`) define como os preços por produto serão informados depois.
      requestBody:
        required: true
        content:
          "application/json":
            schema:
              "$ref": "#/components/schemas/PriceListCreateInput"
            example:
              name: Ouro
              mode: auto
      responses:
        "201":
          description: Tabela criada
          content:
            "application/json":
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  message_code:
                    type: string
                  data:
                    "$ref": "#/components/schemas/PriceList"
              example:
                success: true
                message_code: CREATED
                data:
                  id: 01JZ4W8H2N4XKQ4R9T4YV9AB1C
                  name: Ouro
                  mode: auto
                  position: 0
                  is_active: true
        "401":
          "$ref": "#/components/responses/Unauthorized"
        "403":
          "$ref": "#/components/responses/Forbidden"
        "422":
          "$ref": "#/components/responses/ValidationError"
      security:
        - integrationToken: []
  "/api/seller/price-lists/settings":
    get:
      tags:
        - Tabelas de Preço (Força de Vendas)
      summary: Consultar Toggle de Tabelas
      description: Retorna se o sistema de tabelas de preço está ligado na loja (`price_lists_enabled`).
      responses:
        "200":
          description: Estado do toggle
          content:
            "application/json":
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  message_code:
                    type: string
                  data:
                    "$ref": "#/components/schemas/PriceListSettings"
              example:
                success: true
                message_code: SUCCESS
                data:
                  price_lists_enabled: true
        "401":
          "$ref": "#/components/responses/Unauthorized"
        "403":
          "$ref": "#/components/responses/Forbidden"
      security:
        - integrationToken: []
    put:
      tags:
        - Tabelas de Preço (Força de Vendas)
      summary: Alterar Toggle de Tabelas
      description: Liga ou desliga o sistema de tabelas de preço na loja. Com `false`, as tabelas existentes são preservadas, mas não podem ser usadas (PUT de preços por produto retorna 400).
      requestBody:
        required: true
        content:
          "application/json":
            schema:
              "$ref": "#/components/schemas/PriceListSettings"
            example:
              price_lists_enabled: false
      responses:
        "200":
          description: Toggle atualizado
          content:
            "application/json":
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  message_code:
                    type: string
                  data:
                    "$ref": "#/components/schemas/PriceListSettings"
              example:
                success: true
                message_code: UPDATED
                data:
                  price_lists_enabled: false
        "401":
          "$ref": "#/components/responses/Unauthorized"
        "403":
          "$ref": "#/components/responses/Forbidden"
        "422":
          "$ref": "#/components/responses/ValidationError"
      security:
        - integrationToken: []
  "/api/seller/price-lists/{id}":
    put:
      tags:
        - Tabelas de Preço (Força de Vendas)
      summary: Atualizar Tabela de Preço
      description: Atualiza nome, modo, ativação e/ou posição da tabela. Campos omitidos não são alterados.
      parameters:
        - name: id
          in: path
          required: true
          description: ID da tabela de preço
          schema:
            type: string
      requestBody:
        required: true
        content:
          "application/json":
            schema:
              "$ref": "#/components/schemas/PriceListUpdateInput"
            example:
              name: Ouro Plus
              is_active: false
              position: 2
      responses:
        "200":
          description: Tabela atualizada
          content:
            "application/json":
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  message_code:
                    type: string
                  data:
                    "$ref": "#/components/schemas/PriceList"
              example:
                success: true
                message_code: UPDATED
                data:
                  id: 01JZ4W8H2N4XKQ4R9T4YV9AB1C
                  name: Ouro Plus
                  mode: auto
                  position: 2
                  is_active: false
        "401":
          "$ref": "#/components/responses/Unauthorized"
        "403":
          "$ref": "#/components/responses/Forbidden"
        "404":
          "$ref": "#/components/responses/NotFound"
        "422":
          "$ref": "#/components/responses/ValidationError"
      security:
        - integrationToken: []
    delete:
      tags:
        - Tabelas de Preço (Força de Vendas)
      summary: Excluir Tabela de Preço
      description: Remove a tabela e, em cascata, todas as faixas de preço dos produtos nela.
      parameters:
        - name: id
          in: path
          required: true
          description: ID da tabela de preço
          schema:
            type: string
      responses:
        "200":
          description: Tabela excluída
          content:
            "application/json":
              example:
                success: true
                message_code: DELETED
                data: null
        "401":
          "$ref": "#/components/responses/Unauthorized"
        "403":
          "$ref": "#/components/responses/Forbidden"
        "404":
          "$ref": "#/components/responses/NotFound"
      security:
        - integrationToken: []
  "/api/products/{sku}/price-lists":
    get:
      tags:
        - Preços por Tabela (Força de Vendas)
      summary: Preços nas Tabelas
      description: |-
        Retorna, para o produto informado, uma entrada por **tabela de preço ativa** da loja (ordenadas pela posição definida no portal), com a precificação do produto em cada uma.
        
        - `active` indica se o produto participa daquela tabela.
        - Produto ainda sem preços numa tabela: `active: false`, `base_price: null` e `tiers` vazio.
        - `base_price` só é preenchido em tabelas de modo `auto` (é a base do cálculo por desconto); no modo `manual` vem `null`.
        - `discount_percent` das faixas segue o modo: preenchido no `auto` (base com `0`), `null` no `manual`.
      parameters:
        - name: sku
          in: path
          required: true
          description: ID do produto
          schema:
            type: string
      responses:
        "200":
          description: Precificação do produto em cada tabela ativa
          content:
            "application/json":
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  message_code:
                    type: string
                  data:
                    type: array
                    items:
                      "$ref": "#/components/schemas/ProductPriceListPricing"
              example:
                success: true
                message_code: SUCCESS
                data:
                  - price_list_id: 01JZ4W8H2N4XKQ4R9T4YV9AB1C
                    name: Atacado SP
                    mode: manual
                    active: true
                    base_price: null
                    tiers:
                      - min_quantity: 1
                        price: 50
                        discount_percent: null
                      - min_quantity: 10
                        price: 45
                        discount_percent: null
                  - price_list_id: 01JZ4W8H2N4XKQ4R9T4YV9AB2D
                    name: Distribuidores
                    mode: auto
                    active: true
                    base_price: 50
                    tiers:
                      - min_quantity: 1
                        price: 50
                        discount_percent: 0
                      - min_quantity: 10
                        price: 45
                        discount_percent: 10
                      - min_quantity: 50
                        price: 40
                        discount_percent: 20
                  - price_list_id: 01JZ4W8H2N4XKQ4R9T4YV9AB3E
                    name: Rede Sul
                    mode: manual
                    active: false
                    base_price: null
                    tiers: []
        "401":
          "$ref": "#/components/responses/Unauthorized"
        "403":
          description: Loja sem a funcionalidade força de vendas (ou sem permissão de escopo)
          content:
            "application/json":
              example:
                success: false
                code: 403
                message_code: FORBIDDEN
                description: Força de vendas não habilitada para esta loja.
                data:
                  reason: feature_disabled
                errors: []
                meta: {}
        "404":
          "$ref": "#/components/responses/NotFound"
      security:
        - integrationToken: []
  "/api/products/{sku}/price-lists/{listId}":
    put:
      tags:
        - Preços por Tabela (Força de Vendas)
      summary: Atualizar Preços na Tabela
      description: |-
        Substitui a precificação do produto na tabela informada (reconciliação): faixas presentes no corpo são criadas/atualizadas e faixas ausentes são **removidas**: envie sempre a lista completa.
        
        Regras:
        - Máximo de **5 faixas** por produto na tabela (422).
        - **Tabela `manual`:** informe `tiers.*.price`; `base_price` e `discount_percent` são ignorados/normalizados para `null`.
        - **Tabela `auto`:** informe `base_price` e `tiers.*.discount_percent`; o `price` de cada faixa é calculado (`base × (1 − desconto/100)`, 2 casas). A faixa base (`min_quantity = 1`) nunca tem desconto e os descontos precisam ser **estritamente progressivos** (422).
        - `active: false` mantém as faixas salvas mas tira o produto da tabela.
        - Sistema de tabelas desligado nas configurações da loja → 400.
        - Nada aqui altera o preço marketplace do produto (`/api/products/{id}/prices`).
      parameters:
        - name: sku
          in: path
          required: true
          description: ID do produto
          schema:
            type: string
        - name: listId
          in: path
          required: true
          description: ID da tabela de preço
          schema:
            type: string
      requestBody:
        required: true
        content:
          "application/json":
            schema:
              "$ref": "#/components/schemas/ProductPriceListPutInput"
            examples:
              manual:
                summary: "Tabela manual: preço digitado por faixa"
                value:
                  active: true
                  tiers:
                    - min_quantity: 1
                      price: 50
                    - min_quantity: 10
                      price: 45
              auto:
                summary: "Tabela auto: base_price + descontos progressivos"
                value:
                  active: true
                  base_price: 50
                  tiers:
                    - min_quantity: 1
                    - min_quantity: 10
                      discount_percent: 10
                    - min_quantity: 50
                      discount_percent: 20
              desativar:
                summary: Tirar o produto da tabela mantendo as faixas salvas
                value:
                  active: false
                  tiers:
                    - min_quantity: 1
                      price: 50
      responses:
        "200":
          description: Precificação atualizada. Retorna o estado pós-reconciliação (active, base_price e tiers).
          content:
            "application/json":
              example:
                success: true
                message_code: RESOURCE_UPDATED
                data:
                  active: true
                  base_price: 50
                  tiers:
                    - min_quantity: 1
                      price: 50
                      discount_percent: 0
                    - min_quantity: 10
                      price: 45
                      discount_percent: 10
                    - min_quantity: 50
                      price: 40
                      discount_percent: 20
        "400":
          description: Sistema de tabelas de preço desativado para a loja
          content:
            "application/json":
              example:
                success: false
                code: 400
                message_code: BAD_REQUEST
                description: Sistema de tabela de preços desativado para esta loja.
                data: []
                errors:
                  - Sistema de tabela de preços desativado para esta loja.
                meta: []
        "401":
          "$ref": "#/components/responses/Unauthorized"
        "403":
          description: Loja sem a funcionalidade força de vendas (ou sem permissão de escopo)
          content:
            "application/json":
              example:
                success: false
                code: 403
                message_code: FORBIDDEN
                description: Força de vendas não habilitada para esta loja.
                data:
                  reason: feature_disabled
                errors: []
                meta: {}
        "404":
          description: Produto ou tabela de preço não encontrados (ou tabela de outra loja)
          content:
            "application/json":
              example:
                success: false
                code: 404
                message_code: NOT_FOUND
                description: Tabela de preço não encontrada.
                data: []
                errors:
                  - Tabela de preço não encontrada.
                meta: []
        "422":
          description: Erro de validação ou regra de negócio da tabela
          content:
            "application/json":
              examples:
                validation:
                  summary: Erro de validação de campos
                  value:
                    success: false
                    code: 422
                    message_code: VALIDATION_ERROR
                    description: Foram encontrados erros de validação na requisição.
                    data: []
                    errors:
                      tiers:
                        - O campo tiers é obrigatório.
                    meta: []
                tier_limit:
                  summary: Mais de 5 faixas na tabela
                  value:
                    success: false
                    code: 422
                    message_code: UNPROCESSABLE_ENTITY
                    description: Máximo de 5 faixas por produto nesta tabela.
                    data: []
                    errors:
                      - Máximo de 5 faixas por produto nesta tabela.
                    meta: []
                discount_not_progressive:
                  summary: Descontos não progressivos (modo auto)
                  value:
                    success: false
                    code: 422
                    message_code: UNPROCESSABLE_ENTITY
                    description: O desconto das faixas deve ser progressivo.
                    data: []
                    errors:
                      - O desconto das faixas deve ser progressivo.
                    meta: []
      security:
        - integrationToken: []
components:
  securitySchemes:
    integrationToken:
      type: http
      scheme: bearer
      description: "Token de Integração da loja (formato: sk_...). Criado em Configurações → Tokens de Integração no painel do seller."
  schemas:
    Product:
      type: object
      properties:
        id:
          type: string
          description: ID do produto (26 caracteres)
        name:
          type: string
        sku:
          type: string
        price_type:
          type: string
          enum:
            - default
            - wholesale
        base_unit:
          type: string
          enum:
            - un
            - kg
            - pc
            - cx
            - pct
            - lt
            - mt
            - m2
            - m3
        dimensions:
          type: object
          properties:
            width:
              type: number
              description: Largura em cm
            height:
              type: number
              description: Altura em cm
            length:
              type: number
              description: Comprimento em cm
            weight:
              type: number
              description: Peso em kg
        is_available_for_sale:
          type: boolean
          description: Indica se o produto está disponível para venda
          default: true
        is_industrializable:
          type: boolean
          description: Indica se o produto pode ser fabricado sob demanda
        auto_wholesale_pricing:
          type: boolean
          description: Indica se os preços das faixas de atacado são derivados automaticamente do percentual de desconto. O preço de cada faixa é arredondado na escala do SKU (`price_scale`)
        advanced_pricing:
          type: boolean
          description: "Preço avançado ligado: o preço unitário do SKU aceita 3 casas decimais (por exemplo, R$ 0,375). Desligado, o preço unitário usa 2 casas"
          default: false
        price_scale:
          type: integer
          enum:
            - 2
            - 3
          readOnly: true
          description: Somente leitura. Quantidade de casas decimais do preço unitário do SKU, derivada de `advanced_pricing` (`false` resulta em `2`, `true` resulta em `3`). Não aceita entrada. Subtotais e valores derivados continuam em 2 casas, arredondados para cima
        min_purchase_quantity:
          type: integer
          minimum: 1
          description: Piso de compra do SKU em unidades físicas (quantidade do item multiplicada pelo multiplicador da variação), nunca em packs
          default: 1
        allow_fractional_quantity:
          type: boolean
          description: "Define o comportamento do piso. `false`: venda apenas em múltiplos exatos de `min_purchase_quantity`. `true`: venda fracionada, bastando alcançar o mínimo"
          default: false
        allow_sale_without_stock:
          type: boolean
          description: Permite venda sem estoque disponível (pedido sob encomenda)
        keep_flat:
          type: boolean
          description: "Empacotamento: trava a rotação no eixo vertical. O item viaja deitado (este lado para cima) e não pode ser girado em pé na cubagem de frete"
          default: false
        ship_isolated:
          type: boolean
          description: "Empacotamento: o SKU sai em volume próprio, nunca dividindo a mesma caixa com outro SKU"
          default: false
        dimensions_formatted:
          type: object
          nullable: true
          description: Dimensões formatadas para exibição (somente se include_dimensions_formatted=true)
          properties:
            width:
              type: string
              nullable: true
              example: 10,00 cm
            height:
              type: string
              nullable: true
              example: 10,00 cm
            length:
              type: string
              nullable: true
              example: 10,00 cm
            weight:
              type: string
              nullable: true
              example: 500g
        quantity:
          type: number
          description: Quantidade total em estoque
        reserved_quantity:
          type: number
          description: Quantidade reservada
        available_quantity:
          type: number
          description: Quantidade disponível para venda (quantity - reserved_quantity)
        price:
          type: number
          description: Preço de venda (somente para price_type=default)
        min_price:
          type: number
          description: Menor preço entre as faixas (somente para price_type=wholesale)
        max_price:
          type: number
          description: Maior preço entre as faixas (somente para price_type=wholesale)
        images_count:
          type: integer
          description: "Quantidade de imagens na galeria do produto. Presente na listagem e no detalhe. O corpo de criação e de atualização não traz este campo: ali vem o array `images` completo"
        thumbnail:
          type: object
          nullable: true
          description: Imagem de destaque do produto (variantes thumbnail e md)
          properties:
            id:
              type: string
              description: ID da imagem
            resources:
              type: array
              items:
                type: object
                properties:
                  name:
                    type: string
                    description: Nome da variante (thumbnail, sm, md, lg)
                  size:
                    type: string
                    description: "Dimensões da variante (ex: 400x400)"
                  url:
                    type: string
                    format: uri
    ProductInput:
      type: object
      required:
        - name
        - sku
        - base_unit
        - price_type
      properties:
        name:
          type: string
          maxLength: 255
        sku:
          type: string
          maxLength: 50
          description: Deve ser único por loja
        base_unit:
          type: string
          enum:
            - un
            - kg
            - pc
            - cx
            - pct
            - lt
            - mt
            - m2
            - m3
        price_type:
          type: string
          enum:
            - default
            - wholesale
        is_available_for_sale:
          type: boolean
          default: true
        is_industrializable:
          type: boolean
          nullable: true
        allow_sale_without_stock:
          type: boolean
          nullable: true
          description: Permite venda sem estoque disponível (pedido sob encomenda)
        auto_wholesale_pricing:
          type: boolean
          description: Opcional. Deriva automaticamente os preços das faixas de atacado a partir do percentual de desconto. O preço de cada faixa é arredondado na escala do SKU (`price_scale`)
        advanced_pricing:
          type: boolean
          default: false
          description: "Opcional. Liga o preço avançado: o preço unitário do SKU passa a aceitar 3 casas decimais (por exemplo, R$ 0,375). Define o `price_scale` retornado (`false` resulta em `2`, `true` resulta em `3`)"
        min_purchase_quantity:
          type: integer
          minimum: 1
          default: 1
          description: Opcional. Piso de compra do SKU em unidades físicas (quantidade do item multiplicada pelo multiplicador da variação), nunca em packs
        allow_fractional_quantity:
          type: boolean
          default: false
          description: "Opcional. `false`: o SKU só é vendido em múltiplos exatos de `min_purchase_quantity` (mínimo 25 aceita 25, 50 e 75, e rejeita 30). `true`: venda fracionada, basta alcançar o mínimo"
        keep_flat:
          type: boolean
          default: false
          description: "Opcional. Empacotamento: trava a rotação no eixo vertical (item viaja deitado, este lado para cima)"
        ship_isolated:
          type: boolean
          default: false
          description: "Opcional. Empacotamento: SKU sai em volume próprio (nunca divide caixa com outro SKU)"
        dimensions:
          type: object
          properties:
            width:
              type: number
              minimum: 0.001
              description: Largura em cm
              nullable: true
            height:
              type: number
              minimum: 0.001
              description: Altura em cm
              nullable: true
            length:
              type: number
              minimum: 0.001
              description: Comprimento em cm
              nullable: true
            weight:
              type: number
              minimum: 0.001
              description: Peso em kg
              nullable: true
          nullable: true
          description: Opcional. Peso e dimensões da unidade. Pode ser omitido ou enviado como null no cadastro; sem medidas o produto não é cotado no frete
    ProductUpdateInput:
      type: object
      description: "Corpo da atualização de produto. Diferente da criação, o `sku` não é aceito (é ignorado se enviado) e apenas `name` é obrigatório: os demais campos, incluindo `dimensions`, mantêm o valor atual quando omitidos."
      required:
        - name
      properties:
        name:
          type: string
          maxLength: 255
        base_unit:
          type: string
          description: Opcional. Mantém o valor atual quando omitido
          enum:
            - un
            - kg
            - pc
            - cx
            - pct
            - lt
            - mt
            - m2
            - m3
        price_type:
          type: string
          description: Opcional. Veja as regras de troca na descrição do endpoint
          enum:
            - default
            - wholesale
        is_available_for_sale:
          type: boolean
          description: Opcional
        is_industrializable:
          type: boolean
          description: Opcional
        allow_sale_without_stock:
          type: boolean
          description: Opcional. Permite venda sem estoque disponível (pedido sob encomenda)
        auto_wholesale_pricing:
          type: boolean
          description: Opcional. Deriva automaticamente os preços das faixas de atacado a partir do percentual de desconto. O preço de cada faixa é arredondado na escala do SKU (`price_scale`)
        advanced_pricing:
          type: boolean
          default: false
          description: "Opcional. Liga o preço avançado: o preço unitário do SKU passa a aceitar 3 casas decimais. Define o `price_scale` retornado (`false` resulta em `2`, `true` resulta em `3`). Ao desligar, faixas com 3 casas já salvas deixam de ser aceitas em novas gravações de preço"
        min_purchase_quantity:
          type: integer
          minimum: 1
          default: 1
          description: Opcional. Piso de compra do SKU em unidades físicas (quantidade do item multiplicada pelo multiplicador da variação), nunca em packs
        allow_fractional_quantity:
          type: boolean
          default: false
          description: "Opcional. `false`: o SKU só é vendido em múltiplos exatos de `min_purchase_quantity` (mínimo 25 aceita 25, 50 e 75, e rejeita 30). `true`: venda fracionada, basta alcançar o mínimo"
        keep_flat:
          type: boolean
          default: false
          description: "Opcional. Empacotamento: trava a rotação no eixo vertical (item viaja deitado, este lado para cima)"
        ship_isolated:
          type: boolean
          default: false
          description: "Opcional. Empacotamento: SKU sai em volume próprio (nunca divide caixa com outro SKU)"
        dimensions:
          type: object
          properties:
            width:
              type: number
              minimum: 0.001
              description: Largura em cm
              nullable: true
            height:
              type: number
              minimum: 0.001
              description: Altura em cm
              nullable: true
            length:
              type: number
              minimum: 0.001
              description: Comprimento em cm
              nullable: true
            weight:
              type: number
              minimum: 0.001
              description: Peso em kg
              nullable: true
          nullable: true
          description: Opcional. Peso e dimensões da unidade; mantém o valor atual quando omitido
    Price:
      type: object
      properties:
        value:
          type: number
          description: Valor unitário da faixa
        min_quantity:
          type: integer
          description: Quantidade mínima para a faixa valer
        discount_percent:
          type: number
          nullable: true
          description: Percentual de desconto da faixa em relação ao preço-base (usado no atacado automático)
    Stock:
      type: object
      properties:
        location_stock:
          type: object
          properties:
            location_id:
              type: string
              description: ID do local de estoque
            store_id:
              type: string
              description: ID da loja
            name:
              type: string
            status:
              type: string
              enum:
                - available
                - unavailable
            location_type:
              type: string
              enum:
                - seller_location
                - fulfillment
            allows_pickup:
              type: boolean
              description: Local permite retirada
            allows_production:
              type: boolean
              description: Local permite produção sob demanda
        quantity:
          type: number
        reserved_quantity:
          type: number
        available_quantity:
          type: number
    PaginationMeta:
      type: object
      properties:
        search_query:
          type: string
        filters:
          type: array
          items:
            type: string
        pagination:
          type: object
          properties:
            page:
              type: integer
            per_page:
              type: integer
            last_page:
              type: integer
            has_prev_page:
              type: boolean
            has_next_page:
              type: boolean
            records:
              type: object
              properties:
                from:
                  type: integer
                to:
                  type: integer
                records:
                  type: integer
    SkuMeasureTier:
      type: object
      properties:
        min_quantity:
          type: integer
          description: Quantidade mínima a partir da qual esta embalagem fechada é usada (sempre >= 2)
        weight:
          type: number
          nullable: true
          description: Peso da embalagem fechada, em kg
        height:
          type: number
          nullable: true
          description: Altura da embalagem fechada, em cm
        width:
          type: number
          nullable: true
          description: Largura da embalagem fechada, em cm
        length:
          type: number
          nullable: true
          description: Comprimento da embalagem fechada, em cm
    SkuMeasureTierInput:
      type: object
      required:
        - min_quantity
      properties:
        min_quantity:
          type: integer
          minimum: 1
          description: Quantidade mínima da faixa. Valor 1 é ignorado (a medida da unidade vive no produto); faixas úteis usam >= 2
        weight:
          type: number
          nullable: true
          minimum: 0
          description: Peso da embalagem fechada, em kg. Obrigatório junto com as demais medidas nas faixas >= 2
        height:
          type: number
          nullable: true
          minimum: 0
          description: Altura da embalagem fechada, em cm
        width:
          type: number
          nullable: true
          minimum: 0
          description: Largura da embalagem fechada, em cm
        length:
          type: number
          nullable: true
          minimum: 0
          description: Comprimento da embalagem fechada, em cm
    ProductSummary:
      type: object
      properties:
        total_products:
          type: integer
          description: Total de produtos no catálogo do seller
        ativos:
          type: integer
          description: Disponíveis para venda
        vendendo:
          type: integer
          description: Ativos com ao menos um anúncio ativo
        disponiveis:
          type: integer
          description: Ativos ainda sem nenhum anúncio
        sem_estoque:
          type: integer
          description: Ativos sem saldo de estoque
        inativos:
          type: integer
          description: Indisponíveis para venda
        industrializaveis:
          type: integer
          description: Fabricados sob demanda
        price_types:
          type: object
          properties:
            standard:
              type: integer
              description: Produtos com preço padrão (default)
            wholesale:
              type: integer
              description: Produtos com preço de atacado (wholesale)
    BulkAssociation:
      type: object
      description: Rastreador de progresso da associação em massa de SKUs.
      properties:
        id:
          type: string
          description: ID do rastreador
        total:
          type: integer
          description: Total de SKUs a processar
        processed:
          type: integer
          description: SKUs processados com sucesso
        failed:
          type: integer
          description: SKUs que falharam
        failures:
          type: array
          description: Últimas falhas registradas (limitado às 100 mais recentes)
          items:
            type: object
            properties:
              sku_id:
                type: string
                description: ID do SKU que falhou
              reason:
                type: string
                description: Motivo da falha
        status:
          type: string
          enum:
            - pending
            - running
            - completed
            - failed
          description: Situação do processamento. `completed` quando termina com ao menos um sucesso; `failed` quando o total falhou
        created_at:
          type: string
          format: date-time
          nullable: true
        updated_at:
          type: string
          format: date-time
          nullable: true
    PriceInput:
      type: object
      properties:
        price:
          type: number
          nullable: true
          minimum: 0
          description: "Valor monetário unitário da faixa, com no máximo `price_scale` casas decimais (2 por padrão; 3 quando o produto tem `advanced_pricing = true`). Enviar 3 casas em um SKU sem `advanced_pricing` retorna 422 de validação. Obrigatório na precificação manual (se omitido vira 0). Em produtos `wholesale` com `auto_wholesale_pricing` ligado é ignorado nas faixas `min_quantity >= 2`: o preço é calculado a partir do preço base e do `discount_percent`. O subtotal da linha e os valores derivados continuam em 2 casas, arredondados para cima."
        min_quantity:
          type: integer
          default: 1
          description: "Quantidade mínima para ativar este preço (Padrão: 1). Produtos `default` só aceitam `min_quantity = 1`."
        discount_percent:
          type: number
          nullable: true
          minimum: 0
          description: Percentual de desconto sobre o preço base unitário (0 a menos de 100). Usado nas faixas de atacado automáticas; os descontos precisam ser progressivos (faixa de quantidade maior exige desconto maior).
          exclusiveMaximum: 100
    PriceList:
      type: object
      description: Tabela de preço da loja (força de vendas).
      properties:
        id:
          type: string
          description: ID da tabela
        name:
          type: string
          description: "Nome exibido (ex.: Ouro, Revenda)"
        mode:
          type: string
          enum:
            - manual
            - auto
          description: "`manual`: preço digitado por faixa; `auto`: preço derivado de base + desconto"
        position:
          type: integer
          description: Ordem de exibição (menor primeiro)
        is_active:
          type: boolean
          description: Se a tabela está ativa na loja
    PriceListCreateInput:
      type: object
      required:
        - name
        - mode
      properties:
        name:
          type: string
          maxLength: 120
          description: Nome da tabela
        mode:
          type: string
          enum:
            - manual
            - auto
          description: Modo de precificação da tabela
    PriceListUpdateInput:
      type: object
      properties:
        name:
          type: string
          maxLength: 120
          description: Novo nome
        mode:
          type: string
          enum:
            - manual
            - auto
          description: Novo modo (em geral imutável se a tabela já tiver produtos)
        is_active:
          type: boolean
          description: Ativa ou desativa a tabela
        position:
          type: integer
          minimum: 0
          description: Nova posição na ordenação
    PriceListSettings:
      type: object
      required:
        - price_lists_enabled
      properties:
        price_lists_enabled:
          type: boolean
          description: Se o sistema de tabelas de preço está ligado na loja
    ProductPriceListPricing:
      type: object
      description: Precificação de um produto numa tabela de preço da loja.
      properties:
        price_list_id:
          type: string
          description: ID da tabela de preço
        name:
          type: string
          description: Nome da tabela definido pelo seller
        mode:
          type: string
          enum:
            - manual
            - auto
          description: "Modo de precificação da tabela: `manual` (preço digitado por faixa) ou `auto` (derivado de base_price − discount_percent)"
        active:
          type: boolean
          description: Indica se o produto participa desta tabela
        base_price:
          type: number
          nullable: true
          description: Preço base usado no modo `auto` para derivar as faixas; `null` no modo `manual`
        tiers:
          type: array
          items:
            type: object
            properties:
              min_quantity:
                type: integer
                description: Quantidade mínima da faixa (a base é 1)
              price:
                type: number
                description: Preço da faixa. No modo `auto` é calculado a partir do base_price e do desconto
              discount_percent:
                type: number
                nullable: true
                description: Desconto da faixa sobre o base_price (modo `auto`; base com 0). `null` no modo `manual`
    ProductPriceListPutInput:
      type: object
      required:
        - active
        - tiers
      properties:
        active:
          type: boolean
          description: Se o produto participa da tabela. `false` mantém as faixas salvas mas tira o produto da tabela
        base_price:
          type: number
          nullable: true
          minimum: 0
          description: Preço base para tabelas de modo `auto`. Ignorado no modo `manual`
        tiers:
          type: array
          minItems: 1
          maxItems: 5
          description: Lista completa de faixas desejada (substitui a existente; faixas ausentes são removidas). Máximo de 5.
          items:
            type: object
            required:
              - min_quantity
            properties:
              min_quantity:
                type: integer
                minimum: 1
                description: Quantidade mínima da faixa; a base é 1
              price:
                type: number
                nullable: true
                minimum: 0
                description: Preço da faixa (modo `manual`). Ignorado no modo `auto`
              discount_percent:
                type: number
                nullable: true
                minimum: 0
                maximum: 99.99
                description: Desconto sobre o base_price (modo `auto`); a faixa base é sempre 0 e os descontos precisam ser progressivos
    ProductStockOverview:
      type: object
      properties:
        total:
          type: integer
          description: Quantidade total de estoque somando todas as localizações.
          example: 150
        stocks:
          type: array
          description: Lista detalhada de estoque do produto em cada localização.
          items:
            "$ref": "#/components/schemas/ProductStockDetail"
        locations:
          type: array
          description: Lista de todas as localizações disponíveis para a loja.
          items:
            "$ref": "#/components/schemas/LocationStock"
    ProductStock:
      type: object
      properties:
        id:
          type: integer
          description: Identificador interno do registro de estoque.
          example: 321
        product_id:
          type: string
          description: ID do produto.
          example: 01H81AV32307PVBSV4RXF15EK9
        location_id:
          type: string
          description: ID da localização de estoque.
          example: 01H81AV32307PVBSV4RXF15LOC
        quantity:
          type: integer
          description: Quantidade atual em estoque.
          example: 50
        reserved_quantity:
          type: integer
          description: Quantidade reservada (não disponível para venda imediata).
          example: 0
        available_quantity:
          type: integer
          description: "Quantidade disponível para venda (quantity - reserved_quantity). É o teto físico: a quantidade comprável pelo cliente ainda é restringida pelo `min_purchase_quantity` e pelo `allow_fractional_quantity` do SKU (veja o guia de Produtos)."
          example: 50
    ProductStockDetail:
      type: object
      description: Registro de estoque do produto em uma localização, com o produto e o local aninhados.
      properties:
        id:
          type: integer
          description: Identificador interno do registro de estoque.
          example: 321
        product_sku:
          type: object
          description: Dados do produto (id, name, sku, base_unit, dimensions, flags de venda, prices, totais de estoque, thumbnail e images).
        location_stock:
          "$ref": "#/components/schemas/LocationStock"
        quantity:
          type: integer
          description: Quantidade física nesta localização.
          example: 100
        reserved_quantity:
          type: integer
          description: Quantidade reservada em carrinhos/pedidos abertos.
          example: 5
        available_quantity:
          type: integer
          description: "Quantidade disponível para venda nesta localização (quantity - reserved_quantity). É o teto físico: a quantidade comprável pelo cliente ainda é restringida pelo `min_purchase_quantity` e pelo `allow_fractional_quantity` do SKU (veja o guia de Produtos)."
          example: 95
    LocationStock:
      type: object
      properties:
        location_id:
          type: string
          description: ID da localização.
          example: 01H81AV32307PVBSV4RXF15LOC
        store_id:
          type: integer
          description: Identificador interno da loja dona do local.
          example: 42
        name:
          type: string
          description: Nome da localização.
          example: Matriz - São Paulo
        status:
          type: string
          enum:
            - available
            - unavailable
          description: Status da localização.
          example: available
        location_type:
          type: string
          enum:
            - seller_location
            - fulfillment
          description: Tipo da localização.
          example: seller_location
        allows_pickup:
          type: boolean
          description: Indica se o local permite retirada em mãos.
          example: false
        allows_production:
          type: boolean
          description: Indica se o local permite produção sob demanda.
          example: false
    StockUpdateInput:
      type: object
      required:
        - location_id
        - quantity
      properties:
        location_id:
          type: string
          description: ID da localização onde o estoque será alterado.
          example: 01H81AV32307PVBSV4RXF15LOC
        quantity:
          type: integer
          minimum: 1
          description: Quantidade a ser definida, incrementada ou decrementada.
          example: 10
        increment:
          type: boolean
          description: Se verdadeiro, soma a quantidade ao estoque atual.
          default: false
          example: false
        decrement:
          type: boolean
          description: Se verdadeiro, subtrai a quantidade do estoque atual.
          default: false
          example: false
    SuccessResponse:
      type: object
      properties:
        success:
          type: boolean
          example: true
        message_code:
          type: string
          example: SUCCESS
        data:
          type: object
        meta:
          type: object
          description: Presente apenas quando há metadados a retornar.
    ErrorResponse:
      type: object
      properties:
        success:
          type: boolean
          example: false
        code:
          type: integer
          example: 400
        message_code:
          type: string
          example: BAD_REQUEST
        description:
          type: string
          example: Erro na requisição.
        data:
          type: array
          items: {}
          example: []
        errors:
          type: object
          additionalProperties:
            type: array
            items:
              type: string
        meta:
          type: array
          items: {}
          example: []
    LocationStockInput:
      type: object
      required:
        - name
        - status
        - location_type
      properties:
        name:
          type: string
          description: Nome do local. Obrigatório. O par nome + `location_type` é único por loja.
        status:
          type: string
          enum:
            - available
            - unavailable
          description: Obrigatório — não há valor assumido. Use `available` para o local já nascer operacional.
        location_type:
          type: string
          enum:
            - seller_location
          description: Obrigatório. Na criação, só `seller_location` é aceito; locais `fulfillment` são criados pela plataforma.
        allows_pickup:
          type: boolean
          default: false
          description: Marca o local como ponto de retirada pelo cliente. Opcional.
        allows_production:
          type: boolean
          default: false
          description: Marca o local como apto a produção sob encomenda. Opcional.
        company_branch_id:
          type: string
          nullable: true
          description: ID da filial a vincular ao local. Precisa ser uma filial ativa da mesma empresa; omita para deixar o local respondendo pela matriz. Consulte os valores em `GET /api/locations-stock/available-branches`.
    CategoriesResponse:
      type: object
      properties:
        success:
          type: boolean
        message_code:
          type: string
        data:
          type: array
          items:
            "$ref": "#/components/schemas/Category"
      required:
        - success
        - message_code
        - data
    Category:
      type: object
      properties:
        id:
          type: integer
          description: ID da categoria
        name:
          type: string
        description:
          type:
            - string
            - "null"
        departament:
          "$ref": "#/components/schemas/Department"
        parent:
          "$ref": "#/components/schemas/ParentCategory"
        is_visible:
          type: boolean
        priority:
          type: integer
        domain:
          type: string
        level:
          type: integer
        hierarchy:
          type: array
          description: Breadcrumb completo da categoria (cada nível com id e nome)
          items:
            "$ref": "#/components/schemas/HierarchyLevel"
        settings:
          type: array
          items: {}
        catalog_overrides:
          type:
            - object
            - "null"
        total_children:
          type: integer
        children:
          type: array
          description: Subcategorias (recursivo). Na busca, só voltam folhas — portanto vazio.
          items:
            "$ref": "#/components/schemas/Category"
      required:
        - id
        - name
        - departament
        - parent
    HierarchyLevel:
      type: object
      properties:
        id:
          type: integer
        name:
          type: string
      required:
        - id
        - name
    Department:
      type: object
      properties:
        id:
          type: integer
          description: ID do departamento
        name:
          type: string
        icon_svg:
          type:
            - string
            - "null"
      required:
        - id
        - name
    ParentCategory:
      type: object
      properties:
        id:
          type: integer
        parent_id:
          type:
            - integer
            - "null"
        name:
          type: string
        hierarchy:
          type: array
          items:
            "$ref": "#/components/schemas/HierarchyLevel"
      required:
        - id
        - parent_id
        - name
    CategoryAttributesResponse:
      type: object
      properties:
        success:
          type: boolean
        message_code:
          type: string
        data:
          type: array
          description: Com `matrix=true`, lista de grupos (`AttributeGroup`). Sem `matrix`, lista plana de atributos (`AttributeItem`).
          items:
            oneOf:
              - "$ref": "#/components/schemas/AttributeGroup"
              - "$ref": "#/components/schemas/AttributeItem"
      required:
        - success
        - message_code
        - data
    AttributeGroup:
      type: object
      properties:
        key:
          type:
            - string
            - "null"
          description: "Chave da matriz (ex: identification, appearance, technical). `null` para atributos sem matriz."
        label:
          type: string
        items:
          type: array
          items:
            "$ref": "#/components/schemas/AttributeItem"
      required:
        - key
        - label
        - items
    AttributeItem:
      type: object
      properties:
        id:
          type: integer
          description: ID do vínculo categoria-atributo
        attribute_id:
          type: integer
          description: ID do atributo
        category_id:
          type: integer
          description: ID da categoria
        is_required:
          type: boolean
          description: Indica se o atributo e obrigatorio para a categoria
        is_feature:
          type: boolean
          description: Indica se e uma caracteristica do produto
        is_variant:
          type: boolean
          description: Indica se e um atributo de variacao
        is_combinable:
          type: boolean
          description: Indica se pode ser usado em combinacoes para variacoes
        is_filter:
          type: boolean
          description: Indica se o atributo aparece como filtro na vitrine
        attribute_data:
          "$ref": "#/components/schemas/AttributeData"
      required:
        - id
        - attribute_id
        - category_id
        - is_required
        - is_feature
        - is_variant
        - is_combinable
        - is_filter
    AttributeData:
      type: object
      description: "Definição do atributo. O bloco `value_definition` é autodescritivo: traz os campos esperados (`fields`), as opções pré-cadastradas (`options`, quando houver) e as unidades disponíveis."
      properties:
        id:
          type: integer
          description: ID do atributo
        name:
          type: string
        type:
          type: string
          enum:
            - default
            - default_unit
            - default_list
            - dimension_3d
            - dimension_2d
            - color
            - image
            - brand
            - packaging
          description: Tipo estrutural do atributo. `default_unit` exige `unit`; `dimension_2d/3d` exigem width/height(/depth)+unit; `color` aceita opção pré-cadastrada ou cor customizada (value + hex).
        value_type:
          type:
            - string
            - "null"
          enum:
            - text
            - float
            - number
            - date
            - boolean
            - select
            - radio
            - checkbox
            - null
          description: Tipo do valor para atributos default. Tipos especializados (color, image, brand, packaging) ignoram este campo.
        is_feature:
          type: boolean
        is_variant:
          type: boolean
        is_calculable:
          type: boolean
          description: "Atributo usado em cálculos (ex: Quantidade, que precisa casar com a unidade base do SKU)"
        domain:
          type:
            - string
            - "null"
        ui:
          type: object
          properties:
            public_name:
              type:
                - string
                - "null"
            description:
              type:
                - string
                - "null"
            matrix:
              type:
                - string
                - "null"
              description: Matriz de organização (identification, appearance, technical, dimensions, ...)
            select_type:
              type: string
              enum:
                - select
                - radio
                - checkbox
                - text
              description: Como o valor é escolhido. `text` = digitação livre; os demais exigem opção pré-cadastrada (exceto atributos de cor, que aceitam cor customizada).
        value_definition:
          type: object
          properties:
            fields:
              type: object
              description: Schema dos campos esperados no valor (montagem dinâmica de formulário)
            options:
              type: array
              description: Opções pré-cadastradas (presente apenas quando o atributo tem opções)
              items:
                "$ref": "#/components/schemas/AttributeOption"
            default_unit:
              type:
                - string
                - "null"
            available_units:
              type: array
              items:
                type: string
    AttributeOption:
      type: object
      properties:
        id:
          type: integer
          description: ID da opção (use em `value_id` ao criar o anúncio)
        attribute_id:
          type: integer
        value:
          type: string
        component:
          type: object
          description: "Payload visual da opção, varia por tipo de atributo. Para cor: `{ value, main_color, hex, brightness }`."
          additionalProperties: true
        position:
          type: integer
        is_default:
          type: boolean
      required:
        - id
        - attribute_id
        - value
        - component
        - position
        - is_default
    AttributeCombinationsResponse:
      type: object
      properties:
        success:
          type: boolean
        message_code:
          type: string
        data:
          type: object
          properties:
            combinations:
              type: array
              items:
                "$ref": "#/components/schemas/AttributeCombination"
            primary_attribute:
              "$ref": "#/components/schemas/PrimaryAttributeInfo"
          required:
            - combinations
            - primary_attribute
      required:
        - success
        - message_code
        - data
    AttributeCombination:
      type: object
      properties:
        attributes:
          type: array
          items:
            "$ref": "#/components/schemas/CombinationAttribute"
        attributes_details:
          type: object
          additionalProperties:
            type: string
        description:
          type: string
      required:
        - attributes
        - attributes_details
        - description
    CombinationAttribute:
      type: object
      properties:
        attribute_id:
          type: integer
          description: ID do atributo
        attribute_name:
          type: string
        value:
          type: string
        unit:
          type: string
          description: Unidade de medida (presente apenas quando aplicável)
        hex:
          type: string
          description: Cor em hexadecimal (presente apenas para atributos de cor)
        main_color:
          type: string
          description: Cor principal (presente apenas para atributos de cor)
        brightness:
          type: string
          enum:
            - light
            - dark
          description: Luminosidade calculada da cor (presente apenas para atributos de cor)
        width:
          type:
            - string
            - number
          description: Largura (presente apenas para atributos de dimensão)
        height:
          type:
            - string
            - number
          description: Altura (presente apenas para atributos de dimensão)
      required:
        - attribute_id
        - attribute_name
        - value
    PrimaryAttributeInfo:
      type: object
      description: Atributo principal das combinações — facilita organizar fotos por variação no front.
      properties:
        id:
          type: integer
          description: ID do atributo principal
        name:
          type:
            - string
            - "null"
        has_options:
          type: boolean
        default_unit:
          type:
            - string
            - "null"
        values:
          type: array
          items:
            type: object
            properties:
              value:
                type: string
              label:
                type: string
              hex:
                type: string
              main_color:
                type: string
              brightness:
                type: string
            required:
              - value
              - label
      required:
        - id
        - name
        - has_options
        - default_unit
        - values
    CreateAdvertisementRequest:
      type: object
      description: É obrigatório informar `seller_sku` (anúncio sem variações) ou `variations` — um dos dois precisa estar presente. Os SKUs devem existir previamente na loja.
      properties:
        title:
          type: string
          maxLength: 100
          description: Titulo do anuncio
        category_id:
          type: integer
          description: ID da categoria selecionada (precisa ser categoria folha existente)
        gtin:
          "$ref": "#/components/schemas/GTIN"
        seller_sku:
          type:
            - string
            - "null"
          description: SKU do produto para anúncio sem variações. Obrigatório quando `variations` não for enviado. O SKU precisa existir na loja.
        attributes:
          type: array
          items:
            "$ref": "#/components/schemas/AdvertisementAttribute"
          description: Atributos de caracteristicas do produto. Obrigatório quando a categoria possui atributos com `is_required=true`.
        variations:
          type: array
          items:
            "$ref": "#/components/schemas/AdvertisementVariation"
          description: Variacoes do produto. Obrigatório quando `seller_sku` não for enviado.
        images:
          type: array
          items:
            "$ref": "#/components/schemas/ImageReference"
          description: Imagens do anuncio principal. Opcional na criação, mas o checklist exige pelo menos uma imagem para publicar.
        description:
          "$ref": "#/components/schemas/PublicationDescription"
          description: Descrição longa do anúncio. Opcional. Pode ser informada na criação ou depois via PUT /api/items/{id}/description.
        technical_sheets:
          type: array
          items:
            "$ref": "#/components/schemas/TechnicalSheetInput"
          description: Fichas técnicas do anúncio. Opcionais. Também podem ser criadas ou editadas depois via /api/items/{id}/technical-sheets.
      required:
        - title
        - category_id
    PublicationDescription:
      type: object
      description: Aceita um objeto `{ layout, raw_content }` ou uma string simples (legado, equivale a `layout=markdown`).
      properties:
        layout:
          type: string
          enum:
            - text
            - html
            - markdown
          description: "Formato do `raw_content`. Default: `markdown`."
        raw_content:
          type: string
          description: Conteúdo bruto da descrição no formato indicado por `layout`.
    TechnicalSheetInput:
      type: object
      required:
        - type
        - title
      properties:
        type:
          type: string
          enum:
            - technical_specification
            - characteristics
            - materials
            - installation
            - maintenance
            - safety
            - environmental
            - custom
          description: Tipo da ficha técnica.
        title:
          type: string
          maxLength: 255
          description: Título exibido para a seção da ficha.
        items:
          type: array
          items:
            type: object
            required:
              - item_key
              - item_value
            properties:
              item_key:
                type: string
                maxLength: 255
              item_value:
                type: string
                maxLength: 1000
              display_order:
                type: integer
                minimum: 0
    GTIN:
      type: object
      properties:
        type:
          type:
            - integer
            - string
          enum:
            - 8
            - 12
            - 13
            - 14
            - "8"
            - "12"
            - "13"
            - "14"
          description: "Tipo de GTIN: 8, 12, 13 ou 14 dígitos (13 para EAN-13). Nas respostas, vem como string."
        value:
          type: string
          description: Número do código de barras, apenas dígitos. A quantidade de dígitos deve ser igual ao `type` e o dígito verificador GS1 é validado. Espaços, pontos e hífens são removidos antes da validação.
      description: "Identificador do produto (código de barras). Usado no schema.org da página e no feed do Google Merchant da loja virtual: sem GTIN nem marca, o item não casa no catálogo do Google."
    AdvertisementAttribute:
      type: object
      description: "Valor de um atributo do anúncio. Campos extras dependem do tipo do atributo: cor customizada usa `hex`/`main_color`/`brightness`; dimensões usam `width`/`height`/`depth` + `unit`; listas usam `items`."
      properties:
        id:
          type: integer
          description: ID do atributo
        value_id:
          type:
            - integer
            - "null"
          description: ID da opção selecionada (para atributos com opcoes pré-cadastradas)
        option_id:
          type:
            - integer
            - "null"
          description: Alias aceito para `value_id`
        value:
          type:
            - string
            - integer
            - "null"
          description: Valor do atributo
        unit:
          type:
            - string
            - "null"
          description: Unidade de medida (obrigatória em atributos do tipo default_unit; precisa estar em available_units)
        hex:
          type:
            - string
            - "null"
          description: "Cor customizada em hexadecimal (#RGB ou #RRGGBB). Para atributos de cor, `value` (nome da cor) + `hex` válido dispensam opção pré-cadastrada."
        main_color:
          type:
            - string
            - "null"
          description: "Cor principal da cor customizada (ex: Vermelho)"
        brightness:
          type:
            - string
            - "null"
          description: Luminosidade da cor (light/dark). Se omitido, é calculada a partir do hex.
        width:
          type:
            - string
            - number
            - "null"
          description: Largura (atributos dimension_2d/dimension_3d)
        height:
          type:
            - string
            - number
            - "null"
          description: Altura (atributos dimension_2d/dimension_3d)
        depth:
          type:
            - string
            - number
            - "null"
          description: Profundidade (atributos dimension_3d)
        fields:
          type:
            - object
            - "null"
          description: "Forma alternativa de enviar dimensões: `{ width, height, depth, unit }`"
          properties:
            width: {}
            height: {}
            depth: {}
            unit:
              type: string
        items:
          type:
            - array
            - "null"
          description: Itens do atributo (obrigatório em atributos do tipo default_list)
    AdvertisementVariation:
      type: object
      properties:
        seller_sku:
          type: string
          description: SKU do produto para esta variacao (precisa existir na loja)
        attributes:
          type: array
          items:
            "$ref": "#/components/schemas/VariationAttribute"
          description: Atributos que definem esta variacao
        images:
          type: array
          items:
            "$ref": "#/components/schemas/ImageReference"
          description: Imagens especificas desta variacao
      required:
        - seller_sku
    VariationAttribute:
      type: object
      description: Informe `attribute_id` e `value` (ou `value_id` para opção pré-cadastrada). Atributos de cor aceitam cor customizada com `value` + `hex` (+ `main_color`/`brightness` opcionais), sem opção pré-cadastrada.
      properties:
        attribute_id:
          type: integer
          description: ID do atributo
        attribute_name:
          type:
            - string
            - "null"
        value:
          type:
            - string
            - integer
            - "null"
        unit:
          type:
            - string
            - "null"
        value_id:
          type:
            - integer
            - "null"
          description: ID da opção pré-cadastrada
        option_id:
          type:
            - integer
            - "null"
          description: Alias aceito para `value_id`
        hex:
          type:
            - string
            - "null"
          description: "Cor customizada em hexadecimal (#RGB ou #RRGGBB)"
        main_color:
          type:
            - string
            - "null"
        brightness:
          type:
            - string
            - "null"
        width:
          type:
            - string
            - number
            - "null"
        height:
          type:
            - string
            - number
            - "null"
        depth:
          type:
            - string
            - number
            - "null"
        fields:
          type:
            - object
            - "null"
          description: "Forma alternativa de enviar dimensões: `{ width, height, depth, unit }`"
    ImageReference:
      type: object
      properties:
        id:
          type: string
          description: ID da imagem (UUID retornado por POST /api/media/upload)
      required:
        - id
    SimulatedAdvertisementResponse:
      type: object
      properties:
        success:
          type: boolean
        message_code:
          type: string
        data:
          "$ref": "#/components/schemas/CreatedAdvertisementData"
      required:
        - success
        - message_code
        - data
    CreatedAdvertisementResponse:
      type: object
      properties:
        success:
          type: boolean
        message_code:
          type: string
        data:
          "$ref": "#/components/schemas/CreatedAdvertisementData"
      required:
        - success
        - message_code
        - data
    CreatedAdvertisementData:
      type: object
      description: Objeto do anúncio, retornado por create, publish e simulate.
      properties:
        item_id:
          type: string
          description: "ID do anúncio: prefixo da plataforma + 13 dígitos (ex: SAM-0000000000007)"
        title:
          type: string
        short_description:
          type:
            - string
            - "null"
        slug:
          type:
            - string
            - "null"
        type:
          allOf:
            - "$ref": "#/components/schemas/TypeValue"
          description: "Tipo do anúncio: `default` (Padrão) ou `catalog` (Catálogo)"
        status:
          allOf:
            - "$ref": "#/components/schemas/StatusValue"
          description: "Status do anúncio: `draft`, `pending_review`, `active`, `paused` ou `inactive`"
        seller_sku:
          type:
            - string
            - "null"
          description: SKU vinculado diretamente ao anúncio (quando não há variações)
        price_range:
          type:
            - array
            - "null"
          description: Faixa de preço calculada a partir das ofertas (pode ser null em rascunhos)
        score:
          type:
            - integer
            - "null"
          description: Score de qualidade do anúncio
        campaigns:
          type: array
        pricing_context:
          type: array
          description: Contexto de precificação por faixa de quantidade (descontos, campanhas)
        frontend_sync_status:
          type: string
          description: "Status de sincronização com a vitrine (ex: pending)"
        last_frontend_sync_at:
          type:
            - string
            - "null"
          description: Formato Y-m-d H:i:s
        submitted_for_review_at:
          type:
            - string
            - "null"
          description: Quando o anúncio foi enviado para revisão. Formato Y-m-d H:i:s
        reviewed_at:
          type:
            - string
            - "null"
          description: Quando a plataforma revisou o anúncio. Formato Y-m-d H:i:s
        rejection_reason:
          type:
            - string
            - "null"
          description: Motivo da rejeição quando o anúncio volta para rascunho
        gtin:
          allOf:
            - "$ref": "#/components/schemas/GTIN"
          description: Presente apenas quando informado A chave é omitida da resposta quando o anúncio não tem GTIN (não vem como null).
        store:
          allOf:
            - "$ref": "#/components/schemas/Store"
          description: Loja dona do anúncio (presente quando carregada)
        department:
          "$ref": "#/components/schemas/Department"
        category:
          "$ref": "#/components/schemas/CategoryRef"
        thumbnail:
          type:
            - object
            - "null"
          description: "Miniatura no tamanho `sm`: `{ id, resource }`"
        images:
          type: array
          items:
            "$ref": "#/components/schemas/Image"
        attributes:
          type: array
          items:
            "$ref": "#/components/schemas/PublicationAttributeOut"
        variations:
          type: array
          items:
            "$ref": "#/components/schemas/PublicationVariationOut"
      required:
        - item_id
        - title
        - type
        - status
        - department
        - category
        - images
        - attributes
        - variations
    Image:
      type: object
      properties:
        id:
          type: string
          description: ID da imagem (UUID)
        resources:
          type: array
          description: Variações de tamanho da imagem (`{ name, url, ... }`)
          items:
            type: object
      required:
        - id
        - resources
    PublicationAttributeOut:
      type: object
      properties:
        attribute_id:
          type: integer
          description: ID do atributo
        attribute_name:
          type:
            - string
            - "null"
        attribute_value:
          type:
            - string
            - integer
            - "null"
        value_unit:
          type:
            - string
            - "null"
        value_id:
          type:
            - integer
            - "null"
        component:
          type:
            - object
            - "null"
          description: "Payload visual do valor. Para cor: `{ value, main_color, hex, brightness }`."
      required:
        - attribute_id
        - attribute_name
        - attribute_value
        - value_unit
        - value_id
        - component
    PublicationVariationOut:
      type: object
      description: Variação do anúncio. Em anúncios padrão o identificador vem como `offer_id`; em anúncios de catálogo, como `option_id`.
      properties:
        offer_id:
          type: string
          description: ID da variação (26 caracteres). Presente em anúncios padrão.
        option_id:
          type: string
          description: ID da opção de catálogo (26 caracteres). Presente em anúncios de catálogo.
        description:
          type: string
          description: "Descrição legível da combinação (ex: Voltagem: 110 V, Cor: Azul)"
        attributes:
          type: array
          description: Atributos da variação (`{ attribute_id, attribute_name, value, unit?, value_id?, hex?, main_color?, brightness? }`)
          items:
            type: object
        status:
          type: string
          enum:
            - active
            - pending
            - hidden
          description: Status da variação
        type:
          type: string
          enum:
            - offer
            - option
        images:
          type: array
          items:
            "$ref": "#/components/schemas/Image"
        seller_sku:
          type:
            - string
            - "null"
          description: Presente quando `type=offer`
        offer:
          "$ref": "#/components/schemas/OfferData"
      required:
        - description
        - attributes
        - status
        - type
        - images
        - offer
    OfferData:
      type: object
      properties:
        price_type:
          type: string
          description: "Tipo de precificação (ex: default, wholesale)"
        price_scale:
          type: integer
          enum:
            - 2
            - 3
          readOnly: true
          description: Casas decimais do preço unitário desta oferta. Vale `3` quando o SKU está com precificação avançada (`advanced_pricing`) ligada, e `2` nos demais casos. Use este valor para formatar e arredondar o preço unitário na sua vitrine.
        min_purchase_quantity:
          type: integer
          readOnly: true
          description: "Piso de compra em unidades físicas. Compare sempre `quantidade escolhida × quantity_multiplier da variação` com este valor: numa variação que agrupa 10 unidades por item, um mínimo de 50 exige 5 itens."
        allow_fractional_quantity:
          type: boolean
          readOnly: true
          description: |-
            Regra de degrau da quantidade mínima.
            
            - **`false`** (venda em múltiplos): quando `min_purchase_quantity` é maior que 1, a compra só é aceita em múltiplos exatos do mínimo. Com mínimo 25, valem 25, 50 e 75; a quantidade 30 é rejeitada.
            - **`true`** (venda fracionada): basta atingir o mínimo. Com mínimo 25, valem 25, 30, 31 e assim por diante.
        price_from:
          type: number
          description: Preço "De" quando há desconto
        prices:
          type: array
          description: Faixas de preço (atacado)
          items:
            type: object
        quantity:
          type: integer
        price:
          type: number
          description: Preço "Por"
        original_price:
          type:
            - number
            - "null"
        has_discount:
          type: boolean
        discount_percent:
          type: integer
        wholesale_tiers:
          type: array
          description: Presente apenas quando há faixas de atacado
          items:
            type: object
        buybox:
          type: object
          description: Dados da buybox (presente apenas em anúncios de catálogo)
      required:
        - price_type
        - price_scale
        - min_purchase_quantity
        - allow_fractional_quantity
        - price_from
        - prices
        - quantity
        - price
        - original_price
        - has_discount
        - discount_percent
    TypeValue:
      type: object
      properties:
        value:
          type: string
          enum:
            - default
            - catalog
        label:
          type: string
      required:
        - value
        - label
    StatusValue:
      type: object
      properties:
        value:
          type: string
          enum:
            - draft
            - pending_review
            - active
            - paused
            - inactive
        label:
          type: string
      required:
        - value
        - label
    Store:
      type: object
      properties:
        store_id:
          type: string
          description: ID da loja (26 caracteres)
        store_name:
          type: string
        store_code:
          type: string
        store_status:
          type: string
        store_url:
          type:
            - string
            - "null"
        store_type:
          type: string
        max_locations:
          type:
            - integer
            - "null"
        features:
          type: array
        company:
          type: object
          description: Dados cadastrais da empresa
        shares_products:
          type: boolean
        shares_stock_locations:
          type: boolean
        shares_logistics:
          type: boolean
        is_primary:
          type: boolean
        primary_store:
          type:
            - object
            - "null"
      required:
        - store_id
        - store_name
        - store_code
    CategoryRef:
      type: object
      properties:
        id:
          type: integer
          description: ID da categoria
        parent_id:
          type:
            - integer
            - "null"
        name:
          type: string
        hierarchy:
          type: array
          items:
            "$ref": "#/components/schemas/HierarchyLevel"
      required:
        - id
        - parent_id
        - name
    AttachSKURequest:
      type: object
      properties:
        seller_sku:
          type: string
          maxLength: 64
          description: SKU do produto a vincular. Precisa pertencer (ou estar associado) à loja autenticada.
        status:
          type: string
          description: "Status inicial da oferta. Default: `active`."
          enum:
            - active
            - paused
            - inactive
      required:
        - seller_sku
    AttachedSKUResponse:
      type: object
      properties:
        success:
          type: boolean
        message_code:
          type: string
        data:
          "$ref": "#/components/schemas/AttachedSKUData"
      required:
        - success
        - message_code
        - data
    AttachedSKUData:
      type: object
      properties:
        offer_id:
          type: string
          description: ID da oferta criada (26 caracteres). Use em PUT/DELETE /api/items/catalog/offers/{offerId}.
        seller_sku:
          type: string
        status:
          type: string
          enum:
            - active
            - paused
            - inactive
            - pending
        is_buybox_winner:
          type: boolean
          description: Recalculado em background após o vínculo — costuma vir `false` na resposta imediata
        last_buybox_calculation:
          type:
            - string
            - "null"
        price_data:
          "$ref": "#/components/schemas/PriceData"
        stock_data:
          "$ref": "#/components/schemas/StockData"
        publication:
          type:
            - object
            - "null"
          description: Resumo do anúncio de catálogo
          properties:
            item_id:
              type: string
              description: ID do anúncio
            title:
              type: string
            status:
              type: string
            type:
              type: string
            thumbnail_id:
              type:
                - string
                - "null"
              description: ID da imagem de capa (UUID)
        option:
          "$ref": "#/components/schemas/OptionData"
      required:
        - offer_id
        - seller_sku
        - status
        - is_buybox_winner
        - last_buybox_calculation
        - price_data
        - stock_data
        - publication
        - option
    PriceData:
      type: object
      description: "Snapshot de preço da oferta. Preço único: `{ price }`. Atacado: `{ type: \"wholesale\", tiers: [...] }`."
      properties:
        price:
          type: number
          description: Preço único (presente quando não há faixas de atacado)
        type:
          type: string
          enum:
            - wholesale
          description: Presente apenas quando há faixas de atacado
        tiers:
          type: array
          description: Faixas de preço por quantidade (presente apenas no atacado)
          items:
            "$ref": "#/components/schemas/PriceTier"
    PriceTier:
      type: object
      properties:
        min_quantity:
          type: integer
        price:
          type: number
      required:
        - min_quantity
        - price
    StockData:
      type: object
      properties:
        id:
          type: integer
          description: ID interno do registro de estoque
        product_id:
          type: string
          description: ID do SKU do produto (26 caracteres)
        location_id:
          type: string
          description: ID do local de estoque (26 caracteres)
        quantity:
          type: integer
        reserved_quantity:
          type: integer
        available_quantity:
          type: integer
      required:
        - id
        - product_id
        - location_id
        - quantity
        - reserved_quantity
        - available_quantity
    OptionData:
      type: object
      properties:
        id:
          type: string
          description: ID da opção de catálogo (26 caracteres)
        description:
          type: string
        attributes:
          type: array
          items:
            "$ref": "#/components/schemas/OptionAttribute"
        type:
          type: string
          enum:
            - option
            - offer
        status:
          type: string
          enum:
            - active
            - pending
            - hidden
        images:
          type: array
          items:
            type: object
      required:
        - id
        - description
        - attributes
        - type
        - status
        - images
    OptionAttribute:
      type: object
      properties:
        attribute_id:
          type: integer
          description: ID do atributo
        attribute_name:
          type: string
        value:
          type: string
        unit:
          type:
            - string
            - "null"
        value_id:
          type:
            - integer
            - "null"
      required:
        - attribute_id
        - attribute_name
        - value
    TechnicalSheet:
      type: object
      description: "Ficha técnica de um anúncio: lista ordenada de pares chave/valor agrupada por tipo (especificações, materiais, segurança etc.)."
      properties:
        sheet_id:
          type: string
          description: ID da ficha técnica (26 caracteres)
        publication_id:
          type: string
          description: "`item_id` do anúncio dono da ficha"
        type:
          type: object
          description: Tipo da ficha técnica
          properties:
            value:
              type: string
              enum:
                - technical_specification
                - characteristics
                - materials
                - installation
                - maintenance
                - safety
                - environmental
                - custom
            label:
              type: string
              description: "Rótulo amigável do tipo (ex.: Ficha Técnica, Materiais)"
            description:
              type: string
          required:
            - value
            - label
            - description
        title:
          type: string
          maxLength: 255
        items:
          type: array
          items:
            "$ref": "#/components/schemas/TechnicalSheetItem"
      required:
        - sheet_id
        - publication_id
        - type
        - title
        - items
    TechnicalSheetItem:
      type: object
      description: Linha da ficha técnica (par chave/valor).
      properties:
        item_id:
          type: string
          description: ID do item da ficha (26 caracteres)
        item_key:
          type: string
          maxLength: 255
          description: "Nome da característica (ex.: Potência)"
        item_value:
          type: string
          maxLength: 1000
          description: "Valor da característica (ex.: 650 W)"
        display_order:
          type: integer
          minimum: 0
          description: Posição de exibição (0 = primeiro)
      required:
        - item_id
        - item_key
        - item_value
        - display_order
    MediaCreatedResponse:
      type: object
      properties:
        success:
          type: boolean
          example: true
        message_code:
          type: string
          example: RESOURCE_CREATED
        data:
          "$ref": "#/components/schemas/MediaItem"
      required:
        - success
        - message_code
        - data
    MediaItemResponse:
      type: object
      properties:
        success:
          type: boolean
          example: true
        message_code:
          type: string
          example: SUCCESS
        data:
          "$ref": "#/components/schemas/MediaItem"
      required:
        - success
        - message_code
        - data
    MediaCollectionResponse:
      type: object
      properties:
        success:
          type: boolean
          example: true
        message_code:
          type: string
          example: SUCCESS
        data:
          type: object
          properties:
            meta:
              "$ref": "#/components/schemas/PaginationMeta"
            data:
              type: array
              items:
                "$ref": "#/components/schemas/MediaItem"
      required:
        - success
        - message_code
        - data
    MediaItem:
      type: object
      description: Objeto que representa uma imagem/mídia na API de seller. Campos internos de propriedade (`owner_id`, `owner_model`) ficam de fora do payload de propósito — você não precisa deles para integrar, então não vai encontrá-los aqui.
      properties:
        id:
          type: string
          description: Identificador único da imagem (26 caracteres). É o valor usado como `{file_hash}` nas rotas de detalhe e exclusão.
          example: 01JMZXZ72881MD2P37N2K97YJJ
        original_source:
          type: string
          format: uri
          description: URL da imagem original, já convertida para WebP em alta qualidade.
          example: https://cdn.example.com/products/2026/05/28/original_01JMZXZ72881MD2P37N2K97YJJ.webp
        urls:
          type: array
          description: "Variações de tamanho geradas automaticamente (todas em WebP). Uma entrada por variação: `thumbnail` (135x135), `sm` (200x200), `md` (400x400), `lg` (800x800) e `xl` (1600x1600)."
          items:
            type: object
            properties:
              name:
                type: string
                enum:
                  - thumbnail
                  - sm
                  - md
                  - lg
                  - xl
                description: Nome da variação de tamanho.
                example: thumbnail
              size:
                type: string
                description: Dimensões máximas da variação, no formato largura x altura.
                example: 135x135
              url:
                type: string
                format: uri
                description: URL pública da variação.
                example: https://cdn.example.com/products/2026/05/28/thumbnail_01JMZXZ72881MD2P37N2K97YJJ.webp
            required:
              - name
              - size
              - url
        in_use:
          type: boolean
          description: Indica se a imagem está associada a algum recurso.
          example: false
        reference_id:
          type: string
          nullable: true
          description: ID do recurso referenciado (se houver).
        reference_model:
          type: string
          nullable: true
          enum:
            - product
            - publication
            - store
          description: Tipo do recurso vinculado, em formato curto. Use o mesmo valor no filtro `filter[reference_model]=...`.
        created_at:
          type: string
          format: date-time
          description: Data de criação da imagem.
          example: "2026-05-28T10:00:00-03:00"
        updated_at:
          type: string
          format: date-time
          description: Data da última atualização da imagem.
          example: "2026-05-28T10:00:00-03:00"
      required:
        - id
        - original_source
        - urls
        - in_use
        - reference_id
        - reference_model
        - created_at
        - updated_at
    MediaCloneFromSkusResponse:
      type: object
      properties:
        success:
          type: boolean
          example: true
        message_code:
          type: string
          example: OK
        data:
          type: object
          properties:
            results:
              type: array
              description: Uma linha por alvo enviado, na mesma ordem do request.
              items:
                "$ref": "#/components/schemas/MediaCloneTargetResult"
          required:
            - results
      required:
        - success
        - data
    ClonedMediaItem:
      type: object
      description: Mídia recém-clonada a partir de uma foto de produto. Nasce sem vínculo (`in_use=false`) — vincule ao anúncio normalmente via `images[].id`; se não for usada, a limpeza diária remove a cópia após ~72 horas.
      properties:
        id:
          type: string
          description: ID da nova mídia (26 caracteres). Use este valor nas `images` do anúncio/variação.
          example: 01JNB2T4G93QRT5V6W7X8Y9Z0A
        source_image_id:
          type: string
          description: ID da imagem de origem (foto da galeria do produto). Útil para rastrear de onde a cópia veio e evitar duplicar sugestões.
          example: 01JMZXZ72881MD2P37N2K97YJJ
        original_source:
          type: string
          format: uri
          description: URL da cópia em alta qualidade (WebP).
          example: https://cdn.example.com/products/2026/06/11/original_01JNB2T4G93QRT5V6W7X8Y9Z0A.webp
        urls:
          type: array
          description: "Variações de tamanho copiadas da imagem de origem (mesmos nomes do upload: `thumbnail`, `sm`, `md`, `lg`, `xl`)."
          items:
            type: object
            properties:
              name:
                type: string
                example: sm
              size:
                type: string
                nullable: true
                example: 200x200
              url:
                type: string
                format: uri
                example: https://cdn.example.com/products/2026/06/11/sm_01JNB2T4G93QRT5V6W7X8Y9Z0A.webp
            required:
              - name
              - url
      required:
        - id
        - source_image_id
        - original_source
        - urls
    MediaCloneTargetResult:
      type: object
      description: Resultado da clonagem de um alvo (`key` + `sku`) enviado no request.
      properties:
        key:
          type: string
          description: A mesma `key` enviada no alvo correspondente.
          example: Azul
        sku:
          type: string
          description: O mesmo `sku` enviado no alvo correspondente.
          example: CAMISETA-AZUL-M
        status:
          type: string
          enum:
            - cloned
            - partial
            - failed
            - no_images
            - sku_not_found
          description: |-
            Resultado deste alvo:
            - `cloned` — todas as fotos da galeria foram copiadas.
            - `partial` — parte foi copiada; o resto está em `errors`.
            - `failed` — o SKU tem galeria, mas nenhuma cópia deu certo.
            - `no_images` — o SKU é da sua loja, mas não tem foto na galeria.
            - `sku_not_found` — o SKU não pertence (ou não está associado) à sua loja.
          example: cloned
        images:
          type: array
          description: Cópias criadas para este alvo. Vazio quando nada foi copiado.
          items:
            "$ref": "#/components/schemas/ClonedMediaItem"
        errors:
          type: array
          description: Fotos da galeria que não puderam ser copiadas. Vazio no caso feliz.
          items:
            "$ref": "#/components/schemas/MediaCloneError"
      required:
        - key
        - sku
        - status
        - images
        - errors
    MediaCloneError:
      type: object
      description: Falha na cópia de uma foto específica da galeria de origem.
      properties:
        image_id:
          type: string
          description: ID da imagem de origem que falhou.
          example: 01JMZXZ72881MD2P37N2K97YJJ
        reason:
          type: string
          enum:
            - source_not_found
            - copy_failed
          description: |-
            - `source_not_found` — a foto está na galeria do SKU, mas o registro de mídia não existe mais.
            - `copy_failed` — falha ao copiar o arquivo no storage.
          example: copy_failed
      required:
        - image_id
        - reason
    SourceMediaItem:
      type: object
      description: Foto da galeria do produto, como ela existe hoje. É a imagem de **origem** — para usá-la em um anúncio, clone antes via `POST /api/media/clone-from-skus`.
      properties:
        id:
          type: string
          description: ID da mídia de origem (26 caracteres).
          example: 01JMZXZ72881MD2P37N2K97YJJ
        original_source:
          type: string
          format: uri
          description: URL da imagem em alta qualidade (WebP).
          example: https://cdn.example.com/products/2026/06/11/original_01JMZXZ72881MD2P37N2K97YJJ.webp
        urls:
          type: array
          description: Variações de tamanho disponíveis (`thumbnail`, `sm`, `md`, `lg`, `xl`).
          items:
            type: object
            properties:
              name:
                type: string
                example: sm
              size:
                type: string
                nullable: true
                example: 200x200
              url:
                type: string
                format: uri
                example: https://cdn.example.com/products/2026/06/11/sm_01JMZXZ72881MD2P37N2K97YJJ.webp
            required:
              - name
              - url
      required:
        - id
        - original_source
        - urls
    MediaSkuGalleryResult:
      type: object
      description: Galeria de um SKU consultado.
      properties:
        sku:
          type: string
          description: O mesmo SKU enviado na consulta.
          example: CAMISETA-AZUL-M
        status:
          type: string
          enum:
            - found
            - no_images
            - sku_not_found
          description: |-
            Resultado desta consulta:
            - `found` — o SKU é da sua loja e tem foto.
            - `no_images` — o SKU é da sua loja, mas a galeria está vazia.
            - `sku_not_found` — o SKU não pertence (ou não está associado) à sua loja.
          example: found
        images:
          type: array
          description: Fotos da galeria, na ordem de exibição (thumbnail primeiro).
          items:
            "$ref": "#/components/schemas/SourceMediaItem"
      required:
        - sku
        - status
        - images
    MediaSkusGalleryResponse:
      type: object
      properties:
        success:
          type: boolean
          example: true
        message_code:
          type: string
          example: OK
        data:
          type: object
          properties:
            results:
              type: array
              description: Uma linha por SKU consultado, na mesma ordem do request (SKUs repetidos são consultados uma vez só).
              items:
                "$ref": "#/components/schemas/MediaSkuGalleryResult"
          required:
            - results
      required:
        - success
        - data
  responses:
    Unauthorized:
      description: Token não fornecido, inválido ou expirado
      content:
        "application/json":
          example:
            success: false
            code: 401
            message_code: UNAUTHORIZED
            description: A autenticação é necessária e falhou ou não foi fornecida.
            data: []
            errors:
              - O access_token da loja é obrigatório no cabeçalho Authorization
            meta: []
    Forbidden:
      description: Sem permissão para acessar este recurso
      content:
        "application/json":
          example:
            success: false
            code: 403
            message_code: FORBIDDEN
            description: Você não tem permissão para acessar este recurso.
            data: []
            errors:
              - "Você não tem permissão para acessar este recurso (escopos ausentes: store_products_write)"
            meta: []
    NotFound:
      description: Recurso não encontrado
      content:
        "application/json":
          example:
            success: false
            code: 404
            message_code: NOT_FOUND
            description: O recurso solicitado não foi encontrado.
            data: []
            errors:
              - Produto não encontrado.
            meta: []
    Duplicated:
      description: "Recurso duplicado (ex: SKU já existe nesta loja)"
      content:
        "application/json":
          example:
            success: false
            code: 409
            message_code: DUPLICATED
            description: Este SKU já está cadastrado para esta loja.
            data: []
            errors:
              - Este SKU já está cadastrado para esta loja.
            meta: []
    ValidationError:
      description: Erro de validação nos campos enviados
      content:
        "application/json":
          example:
            success: false
            code: 422
            message_code: VALIDATION_ERROR
            description: Foram encontrados erros de validação na requisição.
            data: []
            errors:
              sku:
                - O campo SKU é obrigatório.
              name:
                - O campo Nome é obrigatório.
            meta: []
    Conflict:
      description: Conflito. O recurso está em uso e não pode ser removido.
      content:
        "application/json":
          example:
            success: false
            code: 409
            message_code: CONFLICT
            description: A imagem está em uso e não pode ser removida ou alterada.
            data: []
            errors:
              - A imagem está em uso e não pode ser removida ou alterada.
            meta: []
security:
  - integrationToken: []
