Mixes
Lista e abre os mixes liberados para o cliente logado e calcula como o preço fechado de um mix se distribui entre as linhas do carrinho.
Um mix é um conjunto de produtos com preço fechado, montado pela loja ou por um seller para clientes específicos (por exemplo, o sortimento de um cliente B2B). O mix tem componentes, cada um com quantidade, se é obrigatório e, opcionalmente, um grupo de troca (swap_group): quando o mix permite escolha (allow_customer_pick), o cliente pode trocar um componente por outro produto do mesmo grupo.
Os mixes são por cliente: a lista depende de quem está logado, por isso todas as rotas exigem login.
/api/v1/storefront/mix-productsClienteListar mixes visíveis ao cliente
Exige o módulo mix-products ligado e login do cliente. A resposta não é paginada.
Cabeçalhos
AuthorizationstringobrigatórioBearer <token> do cliente.Exemplo de requisição
curl "$BASE_URL/api/v1/storefront/mix-products" \
-H "Authorization: Bearer $TOKEN"Resposta
{
"mixes": [
{
"id": "4b7e2d90-61c3-4f1a-9a0e-2c5d8f3b7a16",
"vendor_id": "94bd7e9a-fdad-4d01-8fc2-ba9d677d8b36",
"name": "Kit Reposição Mensal",
"description": "Os itens de giro do mês.",
"sku": "MIX-001",
"status": "A",
"bundle_price_centavos": 25000,
"currency": "BRL",
"visibility": "customer-list",
"min_components": 1,
"max_components": 2,
"allow_customer_pick": true,
"image_url": "",
"created_at": "2026-09-01T12:00:00Z",
"updated_at": "2026-09-10T09:30:00Z",
"components": [
{
"id": "c1d2e3f4-0a1b-4c2d-8e3f-9a0b1c2d3e4f",
"mix_id": "4b7e2d90-61c3-4f1a-9a0e-2c5d8f3b7a16",
"product_id": "5e833a7f-1a77-402c-835a-32594350d7a7",
"quantity": 2,
"sort_order": 0,
"is_required": true,
"allow_swap": true,
"swap_group": "cafe",
"product_name": "Café em grãos 1 kg",
"product_sku": "CAF-001",
"product_price_centavos": 9000,
"product_image": "/uploads/exemplo/cafe.png"
}
],
"component_subtotal_centavos": 18000,
"savings_centavos": 0
}
]
}Campos da resposta
mixesarraymixes[].iduuidmixes[].vendor_iduuidmixes[].namestringmixes[].descriptionstringmixes[].skustringmixes[].statusstringA nesta rota.mixes[].bundle_price_centavosinteger0 significa mix gratuito.mixes[].currencystringBRL).mixes[].visibilitystringpublicprivatecustomer-listmixes[].min_componentsinteger0 sem mínimo.mixes[].max_componentsinteger0 sem máximo.mixes[].allow_customer_pickbooleantrue se o cliente escolhe os componentes; false se o mix é levado inteiro.mixes[].image_urlstringmixes[].created_atstringmixes[].updated_atstringmixes[].componentsarraymixes[].components[].iduuidchoices[].component_id).mixes[].components[].mix_iduuidmixes[].components[].product_iduuidmixes[].components[].quantityintegermixes[].components[].sort_orderintegermixes[].components[].is_requiredbooleanmixes[].components[].allow_swapbooleanmixes[].components[].swap_groupstringallow_swap) podem substituí-lo. Ausente sem grupo.mixes[].components[].product_namestringmixes[].components[].product_skustringmixes[].components[].product_price_centavosintegermixes[].components[].product_imagestringmixes[].component_subtotal_centavosintegermixes[].savings_centavosintegercomponent_subtotal_centavos menos bundle_price_centavos, nunca negativa.Erros
| Status | Quando | Corpo |
|---|---|---|
| 401 | Sem login. | {"error":"missing authorization header"} (ou invalid authorization format, se o cabeçalho está malformado) |
| 403 | Módulo mix-products desligado. | {"error":"module_disabled","module":"mix-products"} |
| 500 | Falha interna. | {"error":"internal_error","message":"..."} |
/api/v1/storefront/mix-products/{id}ClienteDetalhe de um mix
Mesmo formato de um item da lista. Responde 404 se o mix não existe ou não é visível ao cliente.
Parâmetros de caminho
iduuidobrigatórioExemplo de requisição (detalhe)
curl "$BASE_URL/api/v1/storefront/mix-products/4b7e2d90-61c3-4f1a-9a0e-2c5d8f3b7a16" \
-H "Authorization: Bearer $TOKEN"Resposta (detalhe)
{
"id": "4b7e2d90-61c3-4f1a-9a0e-2c5d8f3b7a16",
"name": "Kit Reposição Mensal",
"sku": "MIX-001",
"status": "A",
"bundle_price_centavos": 25000,
"currency": "BRL",
"visibility": "customer-list",
"min_components": 1,
"max_components": 2,
"allow_customer_pick": true,
"components": [
{
"id": "c1d2e3f4-0a1b-4c2d-8e3f-9a0b1c2d3e4f",
"mix_id": "4b7e2d90-61c3-4f1a-9a0e-2c5d8f3b7a16",
"product_id": "5e833a7f-1a77-402c-835a-32594350d7a7",
"quantity": 2,
"is_required": true,
"allow_swap": true,
"swap_group": "cafe",
"product_price_centavos": 9000
}
],
"component_subtotal_centavos": 18000,
"savings_centavos": 0
}Os campos são os do item da lista, com uma diferença: o detalhe pode trazer também assignments (as atribuições do mix a clientes e grupos). É dado de gestão da loja: não o exiba nem dependa dele.
Erros (detalhe)
| Status | Quando | Corpo |
|---|---|---|
| 401 | Sem login. | {"error":"missing authorization header"} |
| 403 | Módulo desligado. | {"error":"module_disabled","module":"mix-products"} |
| 404 | Mix inexistente ou não visível ao cliente. | {"error":"not_found","message":"mix not found or not visible"} |
/api/v1/storefront/mix-products/{id}/resolveClienteResolver a escolha em linhas de carrinho
Recebe a escolha do cliente e calcula as linhas que o mix gera, com o preço fechado distribuído entre elas proporcionalmente ao preço de tabela. Não grava nada: não adiciona ao carrinho nem reserva estoque. O resultado é a base para o carrinho receber as linhas.
Se o mix não permite escolha (allow_customer_pick: false) e choices vem vazio, a plataforma assume todos os componentes.
Parâmetros de caminho
iduuidobrigatórioCabeçalhos
AuthorizationstringobrigatórioBearer <token> do cliente.Content-Typestringobrigatórioapplication/json.Corpo da requisição
choicesarrayallow_customer_pick: false.choices[].component_iduuidobrigatóriocomponents[].id).choices[].product_iduuidobrigatórioproduct_id do componente, ou outro produto de um componente do mesmo swap_group quando o componente aceita troca.Exemplo de requisição (resolver)
curl -X POST "$BASE_URL/api/v1/storefront/mix-products/4b7e2d90-61c3-4f1a-9a0e-2c5d8f3b7a16/resolve" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"choices":[{"component_id":"c1d2e3f4-0a1b-4c2d-8e3f-9a0b1c2d3e4f","product_id":"5e833a7f-1a77-402c-835a-32594350d7a7"}]}'Resposta (resolver)
{
"mix_id": "4b7e2d90-61c3-4f1a-9a0e-2c5d8f3b7a16",
"bundle_price_centavos": 25000,
"component_subtotal_centavos": 18000,
"savings_centavos": 0,
"lines": [
{
"product_id": "5e833a7f-1a77-402c-835a-32594350d7a7",
"product_name": "Café em grãos 1 kg",
"product_sku": "CAF-001",
"quantity": 2,
"unit_price_centavos": 12500,
"line_total_centavos": 25000,
"mix_id": "4b7e2d90-61c3-4f1a-9a0e-2c5d8f3b7a16",
"component_id": "c1d2e3f4-0a1b-4c2d-8e3f-9a0b1c2d3e4f"
}
]
}Campos da resposta
mix_iduuidbundle_price_centavosintegercomponent_subtotal_centavosintegersavings_centavosintegersubtotal menos preço fechado), nunca negativa.linesarraylines[].product_iduuidlines[].product_namestringlines[].product_skustringlines[].quantityintegerlines[].unit_price_centavosintegerline_total_centavos como valor oficial da linha.lines[].line_total_centavosintegerlines[].mix_iduuidlines[].component_iduuidErros (resolver)
| Status | Quando | Corpo |
|---|---|---|
| 400 | Corpo não é JSON válido. | {"error":"invalid_body","message":"<erro de leitura>"} |
| 400 | Escolha inválida: componente de outro mix, troca não permitida ou fora do grupo, falta de componente obrigatório, menos que min_components ou mais que max_components, ou mix sem escolha com choices diferente de todos os componentes. | {"error":"invalid_input","message":"mix-products: invalid input: minimum 1 components required, got 0"} |
| 401 | Sem login. | {"error":"missing authorization header"} |
| 403 | Módulo desligado. | {"error":"module_disabled","module":"mix-products"} |
| 404 | Mix inexistente ou não visível ao cliente. | {"error":"not_found","message":"mix not found or not visible"} |