Formação de Preço¶
Formação de Preço é o motor de pricing dinâmico da Gubee. Em vez de o seller (ou o ERP) enviar um preço fixo por SKU, ele define uma regra que recalcula o preço final a partir do custo unitário, comissão do marketplace, frete e demais custos variáveis. O motor executa a regra em um job assíncrono e atualiza os preços em todos os anúncios elegíveis.
Esta página explica o modelo para que parceiros e integradores entendam por
que veem alterações de preço que não dispararam — e saibam quando usar o
motor em vez de um simples PUT /prices/bysku.
Por que isto existe?
Sellers com milhares de SKUs em múltiplos marketplaces precisam ajustar preços quando custos mudam. Atualizar SKU por SKU é inviável. A Formação de Preço permite recalcular tudo em lote a partir de um único insumo: o custo unitário.
Por que isto importa para integradores¶
Mesmo que você não vá usar a Formação de Preço no seu ERP, ela afeta o que você observa:
- Você faz
PUT /integration/prices/byskucomvalue: 199.90. - O motor tem uma regra
ACTIVEcujo resultado é209.90. - Após alguns segundos,
GET /integration/prices/bysku?sku=Xretorna209.90, não199.90. Não é bug — a regra sobrescreveu seu preço.
Se você precisa de preço "exato" para reconciliação, leia sempre via
GET /integration/prices/... após o lag de consistência (~30s), nunca confie
no valor que acabou de escrever. Veja
CQRS e consistência eventual.
Conceitos-chave¶
Rule (Regra)¶
Uma PriceFormationRule é a unidade de configuração. Tem:
- Domínio (
domain):PRODUCT(todos os SKUs de um produto) ouAD(anúncio específico). - Base (
baseValue):COST(custo unitário do SKU) ouSALE_PRICE(preço de venda atual). - Elegibilidade (
eligibility): filtros que determinam quais SKUs/anúncios a regra atinge (por categoria, marca, plataforma, etc.). - Estrutura de custos (
costStructure): ver seção abaixo. - Objetivo (
objective):CONTRIBUTION_MARGINouMARKUP. - Status (
status): ciclo de vida (ver abaixo). - Datas (
startDate,endDate): janela de vigência.
Status lifecycle¶
INACTIVE ──(startDate)──> SCHEDULED ──(clock tick)──> ACTIVE ──(endDate)──> FINISHED
^ |
| |
+----------------------------------(manual)-------------------------------------------+
| Status | Significado |
|---|---|
INACTIVE |
Regra criada mas não ativa. Pode ser editada. |
SCHEDULED |
Atingiu startDate mas ainda não foi aplicada (job pendente). |
ACTIVE |
Aplicada; preços refletindo a regra. |
FINISHED |
Atingiu endDate ou foi finalizada manualmente. |
restorePricesOnAutoFinish
Ao finalizar a regra (auto ou manual), o campo
restorePricesOnAutoFinish controla se os preços voltam ao valor
pré-regra (true) ou ficam no último valor calculado (false). Padrão
em UI admin: true.
CostStructure (Estrutura de Custos)¶
A estrutura de custos tem duas camadas:
1. Channel costs (custos de canal)¶
Custos específicos do marketplace onde o anúncio está publicado:
| Campo | Descrição |
|---|---|
commission |
Comissão percentual do marketplace (ex.: 0.16 = 16%). |
freight |
Frete pago pelo canal. |
tax |
Tributos sobre a venda no canal (ex.: ICMS, ISS). |
2. Product costs (custos de produto)¶
Custos associados ao SKU, independentes do canal:
| Campo | Descrição |
|---|---|
inboundFreight |
Frete de entrada (do fornecedor até o CD). |
packaging |
Custo de embalagem. |
royalty |
Royalties (para marcas franqueadas, licenciadas). |
O toggle embedProductCosts (booleano) controla se os custos de produto são
incluídos no cálculo (true) ou ignorados (false, default histórico).
Objective (objetivo)¶
O objetivo define o que a regra busca. Há dois drivers suportados:
| Driver | Fórmula | Quando usar |
|---|---|---|
CONTRIBUTION_MARGIN |
preço = (custo + margem_desejada) / (1 - comissão%) |
Recomendado: lucro por unidade após custos variáveis. |
MARKUP |
preço = custo × markup |
Quando você pensa em multiplicador sobre custo. |
Não há driver PROFIT
Um driver PROFIT (lucro líquido final) foi intencionalmente removido
do modelo. Motivo: lucro real exige conhecer custos fixos da empresa
(aluguel, salários, marketing), que estão fora do escopo da Gubee.
CONTRIBUTION_MARGIN é o substituto correto — mede lucro por unidade
após custos variáveis (comissão, frete, imposto).
Se você vê código-fonte antigo referenciando PROFIT, é legacy morto.
Execution Job¶
A aplicação de uma regra não é síncrona. Ao chamar POST /rules/{id}/apply,
o BFF agenda um RuleExecutionJob que percorre todos os SKUs elegíveis e
recalcula cada preço. Você recebe um jobId para acompanhar progresso.
curl -X POST "$GUBEE_API/bffweb/price-formation/rules/RULE-123/apply" \
-H "Authorization: Bearer $JWT"
Acompanhe o progresso:
curl -X GET "$GUBEE_API/bffweb/price-formation/rules/RULE-123/jobs/JOB-abc-456" \
-H "Authorization: Bearer $JWT"
{
"jobId": "JOB-abc-456",
"status": "RUNNING",
"progressPct": 42,
"processedSkus": 1250,
"totalSkus": 2976,
"errors": []
}
Matriz de decisão: quando usar Formação de Preço¶
| Cenário | Use |
|---|---|
| Ajustar preço de 1 SKU para um valor específico | PUT /integration/prices/bysku |
| Sincronizar preços do ERP para todos os SKUs (preço = preço ERP) | PUT /integration/prices/bysku (loop) |
| Promoção temporária com janela de validade | PROMOTION via /integration/prices/list/bysku |
| Recalcular preços quando custo unitário muda (margem alvo) | Formação de Preço |
| Diferenciar preço por marketplace (mesma margem, custos diferentes) | Formação de Preço (calcula por canal) |
| Ajustar preços sazonalmente (Black Friday, festas) com restauração | Formação de Preço com endDate e restorePricesOnAutoFinish: true |
| Campanha complexa com cupom, desconto fixo, bundle | Promoção nativa do marketplace (não Gubee) |
Regra de ouro
Use Formação de Preço quando o preço é função de custo + margem. Use
PUT /prices/bysku quando o preço é entrada (vem do ERP pronto).
Misturar os dois leva a resultados imprevisíveis (cada write sobrescreve
o outro).
Fluxo de execução¶
- Criação: seller define a regra no painel admin (UI Angular) ou via
POST /bffweb/price-formation/rules. Status inicial:INACTIVE. - Agendamento: ao atingir
startDateou ao chamar/apply, status viraSCHEDULEDe oRuleExecutionJobé enfileirado. - Execução: o job percorre SKUs elegíveis. Para cada um:
- Lê custo unitário (
costStructure.cost). - Lê custos de canal por marketplace (
commission,freight,tax). - Aplica a fórmula do
objective(CONTRIBUTION_MARGINouMARKUP). - Grava novo preço no modelo de escrita (evento de domínio).
- Convergência: modelo de leitura converge em ~30s. Preço visível em
GET /integration/prices/...reflete a regra. - Notificação: webhook
price.updatedé disparado para cada SKU afetado. - Conclusão: ao atingir
endDate, status viraFINISHED. SerestorePricesOnAutoFinish: true, outro job restaura os preços pré-regra.
[CREATE] POST /rules [APPLY] POST /rules/{id}/apply
| |
v v
+----------+ +----------+
| INACTIVE | ─── startDate ──> | SCHEDULED|
+----------+ +----------+
|
v
+----------------+
| RuleExecutionJob| ── async
+----------------+
|
v
+----------+
| ACTIVE | ── preços recalculados
+----------+
|
| endDate
v
+----------+
| FINISHED | ── restorePricesOnAutoFinish?
+----------+ sim -> volta ao pré-regra
Cobertura de dados de custo¶
A regra só pode calcular preço para SKUs que têm custo unitário cadastrado.
Se muitos SKUs da elegibilidade não têm custo, a regra pula esses itens. O
campo costDataCoveragePct no summary da regra mostra o percentual coberto:
curl -X GET "$GUBEE_API/bffweb/price-formation/rules/RULE-123/summary" \
-H "Authorization: Bearer $JWT"
{
"ruleId": "RULE-123",
"status": "ACTIVE",
"eligibleSkus": 5000,
"costDataCoveragePct": 78,
"lastJobId": "JOB-abc-456",
"lastJobStatus": "SUCCESS"
}
Alerta de cobertura baixa
O dashboard alerta quando costDataCoveragePct < 50. Nesse caso, mais da
metade dos SKUs elegíveis não terá preço recalculado — a regra pode dar
resultados enganosos (alguns preços desatualizados misturados com
atualizados). Antes de ativar uma regra em produção, sincronize os custos
no ERP via PUT /integration/products/* ou via planilha.
Onde a Formação de Preço vive na API¶
A Formação de Preço não está na superfície /integration. Ela vive em:
Recursos principais:
| Método | Path | Descrição |
|---|---|---|
POST |
/bffweb/price-formation/rules |
Cria regra. |
GET |
/bffweb/price-formation/rules |
Lista regras (filtros por status). |
GET |
/bffweb/price-formation/rules/{ruleId} |
Detalhe de uma regra. |
PUT |
/bffweb/price-formation/rules/{ruleId} |
Atualiza regra. |
DELETE |
/bffweb/price-formation/rules/{ruleId} |
Remove regra. |
POST |
/bffweb/price-formation/rules/{ruleId}/apply |
Aplica regra (agenda job). |
POST |
/bffweb/price-formation/rules/{ruleId}/clone |
Clona regra. |
POST |
/bffweb/price-formation/rules/{ruleId}/finish |
Finaliza regra manualmente. |
POST |
/bffweb/price-formation/rules/{ruleId}/restore-prices |
Restaura preços pré-regra. |
GET |
/bffweb/price-formation/rules/{ruleId}/summary |
Summary com cobertura. |
GET |
/bffweb/price-formation/rules/{ruleId}/results |
Resultados detalhados por SKU. |
GET |
/bffweb/price-formation/rules/{ruleId}/jobs/{jobId} |
Status de um job. |
POST |
/bffweb/price-formation/rules/{ruleId}/simulate |
Simula regra sem gravar (dry-run). |
POST |
/bffweb/price-formation/simulate |
Simulação ad-hoc. |
Existem também endpoints de templates reutilizáveis:
| Método | Path | Descrição |
|---|---|---|
POST |
/bffweb/price-formation/templates |
Cria template. |
GET |
/bffweb/price-formation/templates |
Lista templates. |
POST |
/bffweb/price-formation/templates/{id}/copy |
Copia template (para outro seller). |
Superfície /bffweb vs /integration
Estes endpoints estão em /bffweb (superfície web). Parceiros ERP/hub
normalmente não consomem a Formação de Preço diretamente — eles apenas
leem o preço resultante via GET /integration/prices/.... Esta página
existe para que você entenda por que vê preços mudando sem ter
enviado update.
Se você precisa automatizar a criação de regras, fale com o time Gubee
para validar se o token de parceiro tem escopo bffweb:price-formation.
O que o integrador deve fazer¶
Na prática, um ERP/hub integrando com a Gubee:
- Não cria regras de Formação de Preço (essa é uma decisão comercial do seller, feita no painel admin).
- Sincroniza custos — se você fornece custo unitário ao produto, a regra consegue calcular. Veja Sincronizar Catálogo.
- Lê preços efetivos via
GET /integration/prices/bysku?sku=X(ou/platforms/{platform}/{itemId}). - Reage a webhooks
price.updatedse precisar manter espelho no ERP.
Para o fluxo simples de atualização de preço (sem Formation), veja Estoque e preço por SKU.
Simulação antes de aplicar¶
Antes de ativar uma regra em produção, use o endpoint de simulação para ver quais seriam os preços resultantes sem gravar:
curl -X POST "$GUBEE_API/bffweb/price-formation/simulate" \
-H "Authorization: Bearer $JWT" \
-H "Content-Type: application/json" \
-d '{
"domain": "PRODUCT",
"baseValue": "COST",
"costStructure": {
"channelCosts": { "commission": 0.16, "freight": 5.00, "tax": 0.18 },
"embedProductCosts": true
},
"objective": {
"driver": "CONTRIBUTION_MARGIN",
"value": 25.00
},
"eligibility": { "platforms": ["MERCADOLIVRE"] }
}'
Resposta (SimulatedResultApiDTO):
{
"totalSkus": 1250,
"simulatedPrices": [
{
"skuId": "SKU-001",
"platform": "MERCADOLIVRE",
"currentPrice": 209.90,
"simulatedPrice": 219.50,
"delta": 9.60
}
]
}
Use a simulação para validar com o time comercial antes de POST /apply.
Erros comuns¶
Preço no ERP não reflete no marketplace¶
Sintoma: ERP envia PUT /prices/bysku com 199.90, mas
GET /integration/prices/MERCADOLIVRE/ITEM retorna 209.90.
Causa: existe regra ACTIVE cujo resultado é 209.90. Toda vez que você
grava um preço, o motor pode regravar com base na regra.
Solução: verifique regras ativas com
GET /bffweb/price-formation/rules?status=ACTIVE. Se houver, pause-a
(POST /rules/{id}/finish) ou ajuste a regra para respeitar override manual.
costDataCoveragePct baixo¶
Sintoma: regra ACTIVE mas só 20% dos SKUs têm preço atualizado.
Causa: 80% dos SKUs elegíveis não têm custo unitário cadastrado.
Solução: sincronize custos via PUT /integration/products/* ou via
planilha admin, e re-apply a regra.
PROFIT em código legado¶
Sintoma: ao ler regras antigas, algumas têm objective.driver: "PROFIT".
Causa: modelo anterior. Não é mais aceito em criação/edição.
Solução: migre para CONTRIBUTION_MARGIN. O BFF rejeita PROFIT em
POST/PUT /rules com 400 Bad Request.
Próximos passos¶
- Estoque e preço por SKU: fluxo simples
de
PUT /prices/bysku(quando você não usa Formation). - CQRS e consistência eventual: por que o preço demora ~30s para refletir.
- Ad vs Produto: como uma regra de domínio
ADdifere dePRODUCT. - Glossário: tradução de Margem de Contribuição, Markup e demais termos para campos da API.
Apêndice: API de Promoções / Ajustes de Preço¶
A Formação de Preço é o motor de precificação dinâmica (calcula preço a
partir de custo + margem desejada). Quando você precisa de ajustes pontuais
de preço (campanhas sazonais, Black Friday, queima de estoque), a Gubee
expõe o CRUD de Promoções em /integration/promotions.
Diferença entre Formation, Promotion e PUT /prices/bysku¶
| Recurso | Quando usar | Efeito |
|---|---|---|
PUT /integration/prices/bysku |
Ajuste direto de 1 SKU, sem regra | Sobrescreve o preço com valor fixo |
Formação de Preço (/bffweb/price-formation/*) |
Regra durável de cálculo de preço | Recalcula preço de N SKUs sempre que custo muda |
Promotion (/integration/promotions/*) |
Campanha temporária com data de início/fim | Aplica desconto/ajuste durante janela configurada |
A Promotion é a única das três que é totalmente exposta na API pública de
integração (a Formação fica apenas no /bffweb porque é operada via UI
interna; o resultado dela é lido pelos parceiros via GET /prices/...).
Endpoints disponíveis¶
Todos sob /integration/promotions (HAL+JSON, autenticação JWT):
| Método | Path | Descrição |
|---|---|---|
GET |
/{promotionId} |
Recupera 1 promoção por ID |
GET |
/list/all?page=&size=&sort= |
Lista paginada (todas do seller) |
POST |
/list/search |
Busca paginada por filtro (status, datas, tipo, marketplace) |
POST |
/list/search/ids |
Mesma busca, mas retorna só IDs (sync incremental) |
POST |
(root) | Cria nova promoção (201 Created) |
PUT |
/update/{promotionId} |
Atualiza promoção existente |
DELETE |
/{promotionId} |
Remove promoção |
PUT |
/activate |
Ativa lote de promoções (transição p/ ACTIVE) |
PUT |
/finish |
Finaliza lote (transição p/ FINISHED) |
POST |
/{id}/clone |
Clona promoção (novo ID, status INACTIVE) |
Ciclo de vida de uma promoção¶
INACTIVE ──activate──> ACTIVE ──finish (manual ou endDate)──> FINISHED
│ │
│ └── se restorePricesOnAutoFinish=true,
│ preços voltam ao valor anterior
│
└── criada via POST ou via clone
Exemplo: criar uma campanha de Black Friday¶
curl -X POST "$GUBEE_API/integration/promotions" \
-H "Authorization: Bearer $JWT" \
-H "Content-Type: application/json" \
-d '{
"name": "Black Friday 2026 - Eletrônicos",
"status": "INACTIVE",
"startDate": "2026-11-24T00:00:00Z",
"endDate": "2026-11-30T23:59:59Z",
"restorePricesOnAutoFinish": true,
"eligibility": {
"domain": "PRODUCT",
"categories": ["Eletrônicos"]
},
"ruleType": "PERCENTAGE_DISCOUNT",
"value": 15.0
}' | jq
Ative em lote próximo ao início:
curl -X PUT "$GUBEE_API/integration/promotions/activate" \
-H "Authorization: Bearer $JWT" \
-H "Content-Type: application/json" \
-d '["<promotionId-retornado>"]' -w "\nHTTP %{http_code}\n"
Para finalizar manualmente antes do endDate:
curl -X PUT "$GUBEE_API/integration/promotions/finish" \
-H "Authorization: Bearer $JWT" \
-H "Content-Type: application/json" \
-d '["<promotionId>"]'
Modelo de dados (resumo)¶
A PromotionDTO tem campos principais:
id,name,status(INACTIVE / ACTIVE / FINISHED)startDate,endDate(ISO 8601)restorePricesOnAutoFinish— setrue, restaura preços pré-promoção ao expirareligibility— árvore de filtros (PRODUCT ou AD, categorias, SKUs, plataformas)ruleType— tipo de ajuste (PERCENTAGE_DISCOUNT, FIXED_DISCOUNT, FIXED_PRICE, etc)value— valor do ajuste (percentual ou absoluto conforme ruleType)
Para schema completo, consulte a Referência da API na árvore lateral.