{"openapi":"3.0.3","info":{"title":"Katalog API","description":"API pública do Katalog para integração de sistemas externos.","version":"1.0.0","contact":{"name":"Suporte Katalog","url":"https://wa.me/5561998031899"}},"servers":[{"url":"https://katalog.com.br/api/v1","description":"Produção"}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"Token de API (gerado em /painel/api-tokens)"}},"schemas":{"Erro":{"type":"object","properties":{"erro":{"type":"string"}}},"Produto":{"type":"object","properties":{"id":{"type":"string"},"nome":{"type":"string"},"descricao":{"type":"string","nullable":true},"preco":{"type":"number"},"precoPromocional":{"type":"number","nullable":true},"imagens":{"type":"array","items":{"type":"string"}},"codigoBarras":{"type":"string","nullable":true},"sku":{"type":"string","nullable":true},"codIntegracao":{"type":"integer","nullable":true,"description":"Código (ERP) — identificador inteiro do produto no sistema externo (Integreon, ERP, etc)."},"url":{"type":"string","format":"uri","description":"URL pública absoluta do produto (canonical do storefront). Usa `https://{slug}.katalog.com.br/produto/{id}` ou o domínio personalizado da loja quando configurado. Útil pra integrador montar link 'Ver no Katalog'."},"disponivel":{"type":"boolean"},"destaque":{"type":"boolean"},"ordem":{"type":"integer"},"controlaEstoque":{"type":"boolean"},"quantidadeEstoque":{"type":"number"},"estoqueMinimo":{"type":"number"},"unidade":{"type":"string","nullable":true,"description":"Unidade de medida (UN, KG, L, PC, etc)."},"multiploVenda":{"type":"number","nullable":true,"description":"Múltiplo de venda (ex: parafuso vendido de 10 em 10 → 10). Quando >1, qualquer `quantidade` enviada em POST /pedidos é arredondada PARA CIMA pro próximo múltiplo. Use esse campo para mostrar a regra ao usuário antes de enviar."},"peso":{"type":"number","nullable":true,"description":"Peso em kg"},"altura":{"type":"number","nullable":true,"description":"Altura em cm"},"largura":{"type":"number","nullable":true,"description":"Largura em cm"},"comprimento":{"type":"number","nullable":true,"description":"Comprimento em cm"},"categoriaId":{"type":"string"},"categoria":{"type":"object","nullable":true,"properties":{"id":{"type":"string"},"nome":{"type":"string"}}},"variacoes":{"type":"array","items":{"$ref":"#/components/schemas/Variacao"}}}},"Webhook":{"type":"object","properties":{"id":{"type":"string"},"nome":{"type":"string"},"url":{"type":"string","format":"uri"},"eventos":{"type":"array","items":{"type":"string","enum":["pedido.criado","pedido.status","pedido.pago","pedido.cancelado","cliente.criado","produto.atualizado","produto.removido","produto.estoque_baixo"]}},"ativo":{"type":"boolean"},"ultimaEntrega":{"type":"string","format":"date-time","nullable":true},"criadoEm":{"type":"string","format":"date-time"}}},"Variacao":{"type":"object","properties":{"id":{"type":"string"},"produtoId":{"type":"string"},"nome":{"type":"string"},"valor1":{"type":"string"},"valor2":{"type":"string","nullable":true},"preco":{"type":"number","nullable":true},"sku":{"type":"string","nullable":true},"codigoBarras":{"type":"string","nullable":true},"codIntegracao":{"type":"integer","nullable":true,"description":"Código (ERP) da variação no sistema externo."},"quantidadeEstoque":{"type":"number"},"estoqueMinimo":{"type":"number"}}},"ItemComplemento":{"type":"object","description":"Item de um grupo de complemento (sabor, adicional, etc).","required":["nome","preco"],"properties":{"nome":{"type":"string"},"preco":{"type":"number"},"estoque":{"type":"integer","nullable":true},"codIntegracao":{"type":"integer","nullable":true,"description":"Código (ERP) do item de complemento no sistema externo."}}},"GrupoComplemento":{"type":"object","properties":{"id":{"type":"string"},"nome":{"type":"string"},"tipo":{"type":"string","enum":["SELECAO","MULTIPLA"]},"obrigatoria":{"type":"boolean"},"minimo":{"type":"integer"},"maximo":{"type":"integer"},"ordem":{"type":"integer"},"itens":{"type":"array","description":"Lista de itens (armazenada como JSON). Cada item segue o schema ItemComplemento.","items":{"$ref":"#/components/schemas/ItemComplemento"}},"criadoEm":{"type":"string","format":"date-time"},"atualizadoEm":{"type":"string","format":"date-time"}}},"Categoria":{"type":"object","properties":{"id":{"type":"string"},"nome":{"type":"string"},"descricao":{"type":"string","nullable":true},"ordem":{"type":"integer"},"ativa":{"type":"boolean"},"criadoEm":{"type":"string","format":"date-time"},"atualizadoEm":{"type":"string","format":"date-time"}}},"Cliente":{"type":"object","properties":{"id":{"type":"string"},"nome":{"type":"string"},"documento":{"type":"string"},"tipoDocumento":{"type":"string","enum":["CPF","CNPJ"]},"email":{"type":"string","nullable":true},"telefone":{"type":"string","nullable":true},"celular":{"type":"string","nullable":true},"cep":{"type":"string","nullable":true},"endereco":{"type":"string","nullable":true},"numero":{"type":"string","nullable":true},"bairro":{"type":"string","nullable":true},"cidade":{"type":"string","nullable":true},"uf":{"type":"string","nullable":true}}},"Pedido":{"type":"object","description":"Os campos `cliente*` (clienteNome, clienteCelular, etc) são o snapshot do cliente no momento do pedido — mesmo quando o cliente não está cadastrado (`cliente: null`), esses campos sempre vêm preenchidos e devem ser usados para identificação.","properties":{"id":{"type":"string"},"numero":{"type":"integer"},"status":{"type":"string","enum":["PENDENTE","CONFIRMADO","PREPARANDO","PRONTO","ENVIADO","ENTREGUE","CANCELADO"]},"tipoEntrega":{"type":"string"},"subtotal":{"type":"number"},"desconto":{"type":"number"},"taxaEntrega":{"type":"number"},"total":{"type":"number"},"observacoes":{"type":"string","nullable":true},"motivoCancelamento":{"type":"string","nullable":true},"tempoEntrega":{"type":"string","nullable":true},"codigoRastreio":{"type":"string","nullable":true},"urlRastreio":{"type":"string","nullable":true},"pagamentoStatus":{"type":"string","nullable":true},"clienteNome":{"type":"string"},"clienteCelular":{"type":"string","nullable":true},"clienteEmail":{"type":"string","nullable":true},"clienteDocumento":{"type":"string","nullable":true},"clienteEndereco":{"type":"string","nullable":true},"clienteBairro":{"type":"string","nullable":true},"clienteCidade":{"type":"string","nullable":true},"clienteUf":{"type":"string","nullable":true},"clienteCep":{"type":"string","nullable":true},"clienteNumero":{"type":"string","nullable":true},"clienteComplemento":{"type":"string","nullable":true},"clienteReferencia":{"type":"string","nullable":true},"cliente":{"allOf":[{"$ref":"#/components/schemas/Cliente"}],"nullable":true},"criadoEm":{"type":"string","format":"date-time"},"atualizadoEm":{"type":"string","format":"date-time"}}},"Paginacao":{"type":"object","properties":{"page":{"type":"integer"},"limite":{"type":"integer"},"total":{"type":"integer"},"totalPaginas":{"type":"integer"}}}},"responses":{"Unauthorized":{"description":"Token inválido ou ausente","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Erro"}}}},"RateLimit":{"description":"Limite de requisições excedido","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Erro"}}},"headers":{"Retry-After":{"schema":{"type":"integer"},"description":"Segundos até liberar"}}}}},"security":[{"bearerAuth":[]}],"paths":{"/produtos":{"get":{"summary":"Listar produtos","tags":["Produtos"],"parameters":[{"in":"query","name":"busca","schema":{"type":"string"}},{"in":"query","name":"categoriaId","schema":{"type":"string"}},{"in":"query","name":"apenasDisponiveis","schema":{"type":"boolean"}},{"in":"query","name":"page","schema":{"type":"integer","default":1}},{"in":"query","name":"limite","schema":{"type":"integer","default":50,"maximum":100}}],"responses":{"200":{"description":"Lista paginada","content":{"application/json":{"schema":{"type":"object","properties":{"produtos":{"type":"array","items":{"$ref":"#/components/schemas/Produto"}},"paginacao":{"$ref":"#/components/schemas/Paginacao"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimit"}}},"post":{"summary":"Criar produto","tags":["Produtos"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["nome","preco"],"properties":{"nome":{"type":"string"},"preco":{"type":"number"},"descricao":{"type":"string"},"categoriaId":{"type":"string","description":"Opcional. Resolução: 1) `categoriaId` explícito é usado; 2) senão, se `categoria` (string) vier no body, faz upsert por nome (case-insensitive); 3) senão, cai em 'Sem categoria' automática."},"categoria":{"type":"string","description":"Upsert por nome (case-insensitive na loja). Se a categoria já existe, vincula o produto. Se não existe, cria e vincula. Útil pra ERP/hub sincronizar catálogo sem precisar chamar /categorias antes."},"codigoBarras":{"type":"string"},"sku":{"type":"string"},"codIntegracao":{"type":"integer","nullable":true,"description":"Código (ERP) — inteiro identificador no sistema externo."},"controlaEstoque":{"type":"boolean"},"quantidadeEstoque":{"type":"number"},"disponivel":{"type":"boolean"},"imagens":{"type":"array","items":{"type":"string","format":"uri"},"description":"URLs das imagens. URLs externas (fora do nosso S3/CloudFront) são automaticamente baixadas e re-hospedadas no nosso S3 antes de gravar — assim o catálogo público renderiza sem bloqueio de domínio."},"unidade":{"type":"string","maxLength":10,"description":"Unidade de medida (UN, KG, L, M, PC, etc). Máx 10 chars. Normalizado para maiúsculas. Default: PC."},"multiploVenda":{"type":"number","minimum":0.001,"description":"Múltiplo de venda (ex: 10 = fardo de 10). Quando >1, POST /pedidos arredonda quantidade pro próximo múltiplo. Default: 1."}}}}}},"responses":{"201":{"description":"Produto criado"},"400":{"description":"Erro de validação"},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/produtos/{id}":{"get":{"summary":"Detalhes de um produto","tags":["Produtos"],"parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Produto","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Produto"}}}},"404":{"description":"Não encontrado"}}},"put":{"summary":"Atualizar produto","description":"Atualiza qualquer subconjunto de campos. Dispara webhook `produto.atualizado`.","tags":["Produtos"],"parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","description":"Todos os campos são opcionais — só atualiza os presentes.","properties":{"nome":{"type":"string"},"descricao":{"type":"string","nullable":true},"preco":{"type":"number"},"precoPromocional":{"type":"number","nullable":true},"categoriaId":{"type":"string"},"imagens":{"type":"array","items":{"type":"string"}},"sku":{"type":"string","nullable":true},"codigoBarras":{"type":"string","nullable":true},"codIntegracao":{"type":"integer","nullable":true,"description":"Código (ERP). Envie `null` para limpar."},"controlaEstoque":{"type":"boolean"},"quantidadeEstoque":{"type":"number"},"estoqueMinimo":{"type":"number"},"disponivel":{"type":"boolean"},"destaque":{"type":"boolean"},"ordem":{"type":"integer"},"unidade":{"type":"string","maxLength":10,"description":"Normalizado para maiúsculas, máx 10 chars."},"multiploVenda":{"type":"number","minimum":0.001,"nullable":true,"description":"Múltiplo de venda. Enviar `null` ou `0` reseta pro default (1)."},"peso":{"type":"number","nullable":true},"altura":{"type":"number","nullable":true},"largura":{"type":"number","nullable":true},"comprimento":{"type":"number","nullable":true}}}}}},"responses":{"200":{"description":"Produto atualizado","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Produto"}}}},"400":{"description":"Erro de validação"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"description":"Não encontrado"}}},"delete":{"summary":"Remover produto (soft-delete)","description":"Marca o produto como removido (`deletadoEm = now()` e `disponivel = false`). Dispara webhook `produto.removido`. Idempotente: 404 se já não existir.","tags":["Produtos"],"parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Produto removido","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"removido":{"type":"boolean"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"description":"Não encontrado"}}}},"/produtos/{id}/variacoes":{"get":{"summary":"Listar variações de um produto","tags":["Variações"],"parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Lista de variações","content":{"application/json":{"schema":{"type":"object","properties":{"variacoes":{"type":"array","items":{"$ref":"#/components/schemas/Variacao"}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"description":"Produto não encontrado"}}},"post":{"summary":"Criar variação para o produto","tags":["Variações"],"parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["nome","valor1"],"properties":{"nome":{"type":"string"},"valor1":{"type":"string"},"valor2":{"type":"string","nullable":true},"preco":{"type":"number","nullable":true},"sku":{"type":"string","nullable":true},"codigoBarras":{"type":"string","nullable":true},"codIntegracao":{"type":"integer","nullable":true,"description":"Código (ERP) da variação."},"quantidadeEstoque":{"type":"number"},"estoqueMinimo":{"type":"number"}}}}}},"responses":{"201":{"description":"Variação criada","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Variacao"}}}},"400":{"description":"Erro de validação"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"description":"Produto não encontrado"}}}},"/variacoes/{id}":{"get":{"summary":"Detalhes de uma variação","tags":["Variações"],"parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Variação","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Variacao"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"description":"Não encontrado"}}},"put":{"summary":"Atualizar variação","description":"Todos os campos opcionais — só atualiza os presentes.","tags":["Variações"],"parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"nome":{"type":"string"},"valor1":{"type":"string"},"valor2":{"type":"string","nullable":true},"preco":{"type":"number","nullable":true},"sku":{"type":"string","nullable":true},"codigoBarras":{"type":"string","nullable":true},"codIntegracao":{"type":"integer","nullable":true,"description":"Código (ERP). Envie `null` para limpar."},"quantidadeEstoque":{"type":"number"},"estoqueMinimo":{"type":"number"}}}}}},"responses":{"200":{"description":"Variação atualizada","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Variacao"}}}},"400":{"description":"Erro de validação"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"description":"Não encontrado"}}},"delete":{"summary":"Remover variação","tags":["Variações"],"parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Variação removida","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"removido":{"type":"boolean"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"description":"Não encontrado"}}}},"/grupos-complemento":{"get":{"summary":"Listar grupos de complemento","description":"Grupos de complemento são reutilizáveis e vinculados a vários produtos. Cada item do array `itens` segue o schema `ItemComplemento` e pode trazer seu próprio `codIntegracao`.","tags":["Complementos"],"responses":{"200":{"description":"Lista","content":{"application/json":{"schema":{"type":"object","properties":{"grupos":{"type":"array","items":{"$ref":"#/components/schemas/GrupoComplemento"}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"}}},"post":{"summary":"Criar grupo de complemento","tags":["Complementos"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["nome","itens"],"properties":{"nome":{"type":"string"},"tipo":{"type":"string","enum":["SELECAO","MULTIPLA"],"default":"SELECAO"},"obrigatoria":{"type":"boolean"},"minimo":{"type":"integer"},"maximo":{"type":"integer"},"ordem":{"type":"integer"},"itens":{"type":"array","minItems":1,"items":{"$ref":"#/components/schemas/ItemComplemento"}}}}}}},"responses":{"201":{"description":"Grupo criado","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GrupoComplemento"}}}},"400":{"description":"Erro de validação"},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/grupos-complemento/{id}":{"get":{"summary":"Detalhes do grupo de complemento","tags":["Complementos"],"parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Grupo","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GrupoComplemento"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"description":"Não encontrado"}}},"put":{"summary":"Atualizar grupo de complemento","description":"Se `itens` estiver presente, substitui completamente a lista anterior (não faz merge item-a-item).","tags":["Complementos"],"parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"nome":{"type":"string"},"tipo":{"type":"string","enum":["SELECAO","MULTIPLA"]},"obrigatoria":{"type":"boolean"},"minimo":{"type":"integer"},"maximo":{"type":"integer"},"ordem":{"type":"integer"},"itens":{"type":"array","minItems":1,"items":{"$ref":"#/components/schemas/ItemComplemento"}}}}}}},"responses":{"200":{"description":"Grupo atualizado","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GrupoComplemento"}}}},"400":{"description":"Erro de validação"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"description":"Não encontrado"}}},"delete":{"summary":"Remover grupo de complemento","tags":["Complementos"],"parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Grupo removido","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"removido":{"type":"boolean"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"description":"Não encontrado"}}}},"/pedidos/{id}":{"get":{"summary":"Detalhes do pedido","tags":["Pedidos"],"parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Pedido","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Pedido"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"description":"Não encontrado"}}},"patch":{"summary":"Atualizar status do pedido","description":"Atualiza status, motivo de cancelamento, tempo de entrega e dados de rastreio. Auto-baixa estoque em PENDENTE→CONFIRMADO e auto-restaura ao cancelar pedido confirmado. Dispara webhook `pedido.status` (e `pedido.cancelado` se cancelado).","tags":["Pedidos"],"parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["status"],"properties":{"status":{"type":"string","enum":["PENDENTE","CONFIRMADO","PREPARANDO","PRONTO","ENVIADO","ENTREGUE","CANCELADO"]},"motivoCancelamento":{"type":"string","nullable":true},"tempoEntrega":{"type":"string","nullable":true},"codigoRastreio":{"type":"string","nullable":true},"urlRastreio":{"type":"string","nullable":true}}}}}},"responses":{"200":{"description":"Pedido atualizado","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Pedido"}}}},"400":{"description":"Erro de validação"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"description":"Não encontrado"}}}},"/webhooks":{"get":{"summary":"Listar webhooks da loja","tags":["Webhooks"],"responses":{"200":{"description":"Lista de webhooks (sem secret)","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Webhook"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"}}},"post":{"summary":"Cadastrar webhook","description":"Cria webhook scoped à loja autenticada. O `secret` é gerado pelo servidor (formato `whsec_<48 hex>`) e retornado **apenas neste response** — guarde-o. Eventos disponíveis: `pedido.criado`, `pedido.status`, `pedido.pago`, `pedido.cancelado`, `cliente.criado`, `produto.atualizado`, `produto.removido`, `produto.estoque_baixo`.","tags":["Webhooks"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["nome","url","eventos"],"properties":{"nome":{"type":"string"},"url":{"type":"string","format":"uri"},"eventos":{"type":"array","items":{"type":"string"}}}}}}},"responses":{"201":{"description":"Webhook criado (com secret)","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Webhook"},{"type":"object","properties":{"secret":{"type":"string"}}}]}}}},"400":{"description":"Erro de validação"},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/webhooks/{id}":{"delete":{"summary":"Remover webhook","tags":["Webhooks"],"parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Webhook removido","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"removido":{"type":"boolean"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"description":"Não encontrado"}}}},"/categorias":{"get":{"summary":"Listar categorias","tags":["Categorias"],"responses":{"200":{"description":"Lista","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Categoria"}}}}}}},"post":{"summary":"Criar categoria","description":"Cria categoria na loja autenticada. Nome único por loja (case-insensitive). `ordem` é opcional — quando omitido, vai pro fim da lista (max+1). Hierarquia Categoria→Subcategoria existe mas é gerenciada em endpoint separado; `categoriaPaiId` no body é ignorado.","tags":["Categorias"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["nome"],"properties":{"nome":{"type":"string"},"descricao":{"type":"string","nullable":true},"ordem":{"type":"integer"},"ativa":{"type":"boolean","default":true}}}}}},"responses":{"201":{"description":"Categoria criada","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Categoria"}}}},"400":{"description":"Nome vazio ou já existe na loja"},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/categorias/{id}":{"get":{"summary":"Detalhes de uma categoria","tags":["Categorias"],"parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Categoria","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Categoria"}}}},"404":{"description":"Não encontrada"}}},"put":{"summary":"Atualizar categoria","description":"Todos os campos são opcionais — só altera os enviados. Renomear pra nome já usado por outra categoria retorna 400.","tags":["Categorias"],"parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"nome":{"type":"string"},"descricao":{"type":"string","nullable":true},"ordem":{"type":"integer"},"ativa":{"type":"boolean"}}}}}},"responses":{"200":{"description":"Categoria atualizada","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Categoria"}}}},"400":{"description":"Validação falhou"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"description":"Não encontrada"}}},"delete":{"summary":"Remover categoria","description":"Bloqueia (409) se houver produtos ou subcategorias vinculados — pra evitar a cascata `onDelete: Cascade` apagar produtos junto. Realoque produtos pra outra categoria antes via `PUT /produtos/{id}`.","tags":["Categorias"],"parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Categoria removida","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"removida":{"type":"boolean"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"description":"Não encontrada"},"409":{"description":"Categoria tem produtos ou subcategorias vinculadas","content":{"application/json":{"schema":{"type":"object","properties":{"erro":{"type":"string"},"produtosVinculados":{"type":"integer"},"subcategoriasVinculadas":{"type":"integer"}}}}}}}}},"/clientes":{"get":{"summary":"Listar clientes","tags":["Clientes"],"parameters":[{"in":"query","name":"busca","schema":{"type":"string"}},{"in":"query","name":"page","schema":{"type":"integer"}},{"in":"query","name":"limite","schema":{"type":"integer"}}],"responses":{"200":{"description":"Lista paginada"}}},"post":{"summary":"Cadastrar cliente (upsert por documento)","tags":["Clientes"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Cliente"}}}},"responses":{"200":{"description":"Cliente atualizado (já existia)"},"201":{"description":"Cliente criado"}}}},"/pedidos":{"get":{"summary":"Listar pedidos","tags":["Pedidos"],"parameters":[{"in":"query","name":"status","schema":{"type":"string","enum":["PENDENTE","CONFIRMADO","PREPARANDO","PRONTO","ENTREGUE","CANCELADO"]}},{"in":"query","name":"desde","schema":{"type":"string","format":"date-time"}},{"in":"query","name":"ate","schema":{"type":"string","format":"date-time"}},{"in":"query","name":"page","schema":{"type":"integer"}},{"in":"query","name":"limite","schema":{"type":"integer"}}],"responses":{"200":{"description":"Lista paginada de pedidos","content":{"application/json":{"schema":{"type":"object","properties":{"pedidos":{"type":"array","items":{"$ref":"#/components/schemas/Pedido"}},"paginacao":{"$ref":"#/components/schemas/Paginacao"}}}}}}}},"post":{"summary":"Criar pedido","description":"Cria um pedido na loja autenticada. Preço é recalculado server-side (variação > promo > preço base) — não confie no valor enviado. Estoque é baixado em transação atômica; se algum item não tem estoque, retorna 400 e nenhuma baixa acontece. Snapshot completo do cliente é gravado no pedido. Dispara webhook `pedido.criado`. Não suporta cupons, cashback, mesas, múltiplas formas de pagamento, frete dinâmico, tabela de preço — use o painel pra isso.","tags":["Pedidos"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["tipoEntrega","cliente","itens"],"properties":{"tipoEntrega":{"type":"string","enum":["DELIVERY","BALCAO","MESA"],"description":"Use BALCAO para retirada no balcão. `RETIRADA` é aceito como alias."},"cliente":{"type":"object","required":["nome"],"description":"Snapshot do cliente. Se `id` for passado, valida que o cliente pertence à loja e usa o cadastro como fallback nos campos não enviados.","properties":{"id":{"type":"string","nullable":true},"nome":{"type":"string"},"celular":{"type":"string","nullable":true},"email":{"type":"string","nullable":true},"documento":{"type":"string","nullable":true},"endereco":{"type":"string","nullable":true},"numero":{"type":"string","nullable":true},"complemento":{"type":"string","nullable":true},"bairro":{"type":"string","nullable":true},"cidade":{"type":"string","nullable":true},"uf":{"type":"string","nullable":true},"cep":{"type":"string","nullable":true},"referencia":{"type":"string","nullable":true}}},"itens":{"type":"array","minItems":1,"items":{"type":"object","required":["produtoId","quantidade"],"properties":{"produtoId":{"type":"string"},"variacaoId":{"type":"string","nullable":true},"quantidade":{"type":"number","minimum":0.001},"observacao":{"type":"string","nullable":true},"opcoes":{"description":"JSON livre — complementos selecionados (`[{ grupo, escolhas: [{ nome, preco }] }, ...]` é a forma usada pelo storefront).","nullable":true}}}},"taxaEntrega":{"type":"number","description":"Valor do frete. Default 0."},"desconto":{"type":"number","description":"Desconto manual. Default 0."},"observacoes":{"type":"string","nullable":true}}}}}},"responses":{"201":{"description":"Pedido criado","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Pedido"}}}},"400":{"description":"Erro de validação (produto não pertence à loja, estoque insuficiente, endereço faltando para DELIVERY, etc)."},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/estoque":{"post":{"summary":"Movimentação de estoque","tags":["Estoque"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["produtoId","tipo","quantidade"],"properties":{"produtoId":{"type":"string"},"tipo":{"type":"string","enum":["ENTRADA","SAIDA","AJUSTE"]},"quantidade":{"type":"number"},"variacaoId":{"type":"string","nullable":true},"observacao":{"type":"string"}}}}}},"responses":{"200":{"description":"Movimentação registrada"},"400":{"description":"Validação falhou"}}}}}}