VenddorDocumentação API
API atualAPI legada
Guia rápidoReferência completa
Carregando índice…
API atual/Referência completa/Cotações e propostas B2B

Negociar em rodadas

Contrapropostas, histórico e aceite de rodadas para cotações negociadas.

Quando a loja liga o módulo enhanced-rfq, cliente e seller trocam propostas em rodadas antes de fechar. Cada rodada é uma cesta completa (produtos, quantidades e preços). Estas cinco rotas exigem os dois módulos, b2b-pricing e enhanced-rfq; sem o segundo, respondem 403 module_disabled.

Fazer contraproposta

Abre uma nova rodada de negociação com a cesta e os preços que o cliente quer. A cotação passa a negotiating, as propostas pendentes anteriores viram countered e o total da cotação passa a ser o da nova rodada. A loja define um máximo de rodadas (5 se não configurou).

POST/api/v1/storefront/quotes/{id}/counterCliente

O cliente propõe uma nova cesta e novos preços

Parâmetros de caminho

idintegerobrigatório
Id numérico da cotação.

Corpo da requisição

itemsarrayobrigatório

Cesta proposta. Pelo menos um item.

product_iduuidobrigatório
Produto.
quantityintegerobrigatório
Quantidade, no mínimo 1.
price_centsintegerobrigatório
Preço unitário proposto, em centavos. Não pode ser negativo.
notesstring
Observação da linha.
messagestring
Mensagem para o seller.
expires_atstring
Até quando a proposta vale (RFC 3339). Depois disso a rodada vira expired.

Exemplo de requisição

bash
curl -X POST "$BASE_URL/api/v1/storefront/quotes/42/counter" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "items": [
      {
        "product_id": "c3f92551-9dda-4fc7-b518-353d1c4e4035",
        "quantity": 200,
        "price_cents": 1950
      }
    ],
    "message": "Fechamos por R$ 19,50 a unidade?",
    "expires_at": "2026-10-18T23:59:59Z"
  }'

Resposta

201Rodada criada
json
{
  "id": "9d1c7e52-4a3b-4f60-8e1d-2b5a7c9e1f30",
  "quote_id": 42,
  "round_number": 1,
  "initiated_by": "customer",
  "message": "Fechamos por R$ 19,50 a unidade?",
  "items": [
    {
      "product_id": "c3f92551-9dda-4fc7-b518-353d1c4e4035",
      "quantity": 200,
      "price_cents": 1950
    }
  ],
  "total_cents": 390000,
  "status": "pending",
  "expires_at": "2026-10-18T23:59:59Z",
  "created_at": "2026-10-12T10:30:00Z"
}

Campos da resposta

iduuid
Id da rodada.
quote_idinteger
Cotação a que a rodada pertence.
round_numberinteger
Número da rodada, a partir de 1.
initiated_bystring
customervendoradmin
Quem fez a proposta.
messagestring
Mensagem que acompanha a proposta.
itemsarray
Cesta proposta: product_id, product_name (nas consultas), quantity, price_cents (centavos por unidade) e notes.
total_centsinteger
Soma de price_cents x quantity, em centavos.
statusstring
pendingacceptedrejectedcounteredexpired
countered é uma proposta substituída por outra mais nova.
expires_atstring
Validade da proposta (RFC 3339). Ausente se não houver.
created_atstring
Criação (RFC 3339).

Erros

StatusQuandoCorpo de exemplo
400O id não é um número.{"error":"invalid quote id"}
401Sem token de cliente no cabeçalho Authorization.{"error":"missing authorization header"}
403A cotação é de outro cliente.{"error":"quote does not belong to this user"}
403O módulo enhanced-rfq está desligado nesta loja.{"error":"module_disabled","module":"enhanced-rfq"}
404Cotação inexistente.{"error":"quote not found"}
400Corpo inválido, sem itens, quantidade ou preço inválido, cotação fora de um estado negociável (pending, quoted, negotiating) ou limite de rodadas atingido.{"error":"counter-offer failed","detail":"maximum negotiation rounds (5) exceeded"}

Ver o histórico da negociação

Lista as rodadas em ordem crescente, de quem quer que as tenha feito (cliente, seller ou administrador), junto com o limite e a rodada atual. É a base para a tela de negociação: a rodada pending mais recente é a que espera resposta.

GET/api/v1/storefront/quotes/{id}/negotiationsCliente

Todas as rodadas de uma cotação

Parâmetros de caminho

idintegerobrigatório
Id numérico da cotação.

Exemplo de requisição

bash
curl "$BASE_URL/api/v1/storefront/quotes/42/negotiations" \
  -H "Authorization: Bearer $TOKEN"

Resposta

200Histórico
json
{
  "quote_id": 42,
  "max_rounds": 5,
  "current_round": 1,
  "rounds": [
    {
      "id": "9d1c7e52-4a3b-4f60-8e1d-2b5a7c9e1f30",
      "quote_id": 42,
      "round_number": 1,
      "initiated_by": "customer",
      "message": "Fechamos por R$ 19,50 a unidade?",
      "items": [
        {
          "product_id": "c3f92551-9dda-4fc7-b518-353d1c4e4035",
          "product_name": "Caneta esferográfica azul (cx 50)",
          "quantity": 200,
          "price_cents": 1950
        }
      ],
      "total_cents": 390000,
      "status": "pending",
      "expires_at": "2026-10-18T23:59:59Z",
      "created_at": "2026-10-12T10:30:00Z"
    }
  ]
}

Campos da resposta

quote_idinteger
A cotação.
max_roundsinteger
Máximo de rodadas permitido (5 se a loja não definiu).
current_roundinteger
Número da última rodada aberta; 0 se ninguém propôs ainda.
expires_atstring
Validade geral da negociação (RFC 3339). Ausente se não houver.
roundsarray
Rodadas, no formato descrito em Fazer contraproposta.

Erros

StatusQuandoCorpo de exemplo
400O id não é um número.{"error":"invalid quote id"}
401Sem token de cliente no cabeçalho Authorization.{"error":"missing authorization header"}
403A cotação é de outro cliente.{"error":"quote does not belong to this user"}
403O módulo enhanced-rfq está desligado nesta loja.{"error":"module_disabled","module":"enhanced-rfq"}
404Cotação inexistente.{"error":"quote not found"}

Aceitar uma rodada

Aceita a rodada indicada. A cotação vira accepted, as demais rodadas pendentes viram rejected, e as linhas da cotação são reescritas com a cesta da rodada (quantidades e preços, inclusive itens incluídos ou removidos na negociação). Depois, leve ao carrinho.

POST/api/v1/storefront/quotes/{id}/accept-round/{roundId}Cliente

Fecha a negociação com os preços de uma rodada

Parâmetros de caminho

idintegerobrigatório
Id numérico da cotação.
roundIduuidobrigatório
Id da rodada (campo id de uma rodada).

Exemplo de requisição

bash
curl -X POST "$BASE_URL/api/v1/storefront/quotes/42/accept-round/9d1c7e52-4a3b-4f60-8e1d-2b5a7c9e1f30" \
  -H "Authorization: Bearer $TOKEN"

Resposta

200Rodada aceita
json
{
  "message": "round accepted"
}

Erros

StatusQuandoCorpo de exemplo
400O id não é um número.{"error":"invalid quote id"}
401Sem token de cliente no cabeçalho Authorization.{"error":"missing authorization header"}
403A cotação é de outro cliente.{"error":"quote does not belong to this user"}
403O módulo enhanced-rfq está desligado nesta loja.{"error":"module_disabled","module":"enhanced-rfq"}
404Cotação inexistente.{"error":"quote not found"}
400A rodada não existe nesta cotação ou não está pending.{"error":"failed to accept round","detail":"round is not in pending status (current: A)"}
422A regra de margem da loja recusou o preço da rodada. O corpo traz ok: false e um reason (below_floor, cost_missing ou unmodelled_offer).{"ok":false,"reason":"below_floor"}
A rota não olha quem propôs

O cliente pode aceitar qualquer rodada pending, inclusive a que ele mesmo propôs. Na interface, ofereça "Aceitar" só para rodadas com initiated_by diferente de customer.

Recusar uma rodada

Marca a rodada como rejected, com um motivo opcional que é anexado à mensagem da rodada. A cotação continua negotiating: o cliente pode fazer uma nova contraproposta ou desistir de vez com recusar cotação.

POST/api/v1/storefront/quotes/{id}/reject-round/{roundId}Cliente

Recusa uma proposta do seller sem encerrar a cotação

Parâmetros de caminho

idintegerobrigatório
Id numérico da cotação.
roundIduuidobrigatório
Id da rodada (campo id de uma rodada).

Corpo da requisição

reasonstring
Motivo da recusa. Opcional; um corpo ausente ou inválido é ignorado.

Exemplo de requisição

bash
curl -X POST "$BASE_URL/api/v1/storefront/quotes/42/reject-round/9d1c7e52-4a3b-4f60-8e1d-2b5a7c9e1f30" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "reason": "Abaixo do nosso orçamento"
  }'

Resposta

200Rodada recusada
json
{
  "message": "round rejected"
}

Erros

StatusQuandoCorpo de exemplo
400O id não é um número.{"error":"invalid quote id"}
401Sem token de cliente no cabeçalho Authorization.{"error":"missing authorization header"}
403A cotação é de outro cliente.{"error":"quote does not belong to this user"}
403O módulo enhanced-rfq está desligado nesta loja.{"error":"module_disabled","module":"enhanced-rfq"}
404Cotação inexistente.{"error":"quote not found"}
400A rodada não existe nesta cotação ou não está pending.{"error":"failed to reject round","detail":"round is not in pending status (current: R)"}

Levar ao carrinho

Passo final da negociação: pega a cotação já accepted e coloca os itens no carrinho com os preços acordados. A cotação vira converted. Diferente de levar ao carrinho em um passo, esta rota não aceita sozinha: exige a cotação já aceita.

POST/api/v1/storefront/quotes/{id}/convert-to-cartCliente

Coloca a cotação aceita no carrinho do cliente

Parâmetros de caminho

idintegerobrigatório
Id numérico da cotação.

Exemplo de requisição

bash
curl -X POST "$BASE_URL/api/v1/storefront/quotes/42/convert-to-cart" \
  -H "Authorization: Bearer $TOKEN"

Resposta

200Carrinho preparado
json
{
  "cart_id": "b7a3c1e9-6d24-4f8a-a0b5-93e1c7d2f468",
  "quote_id": 42,
  "items_added": 1,
  "total_cents": 390000
}

Campos da resposta

cart_iduuid
Carrinho que recebeu os itens.
quote_idinteger
A cotação convertida.
items_addedinteger
Linhas colocadas no carrinho.
total_centsinteger
Soma das linhas, em centavos.

Erros

StatusQuandoCorpo de exemplo
400O id não é um número.{"error":"invalid quote id"}
401Sem token de cliente no cabeçalho Authorization.{"error":"missing authorization header"}
403A cotação é de outro cliente.{"error":"quote does not belong to this user"}
403O módulo enhanced-rfq está desligado nesta loja.{"error":"module_disabled","module":"enhanced-rfq"}
404Cotação inexistente.{"error":"quote not found"}
400A cotação não está accepted, expirou ou não tem itens válidos.{"error":"failed to convert quote to cart","detail":"quote must be in 'accepted' status to convert (current: negotiating)"}
Quem responde pelo seller

A resposta do seller acontece no painel dele (API do seller), que não faz parte desta seção. Na vitrine, o cliente enxerga essas respostas como novas rodadas em negotiations.

Veja também

  • Decidir uma cotação
  • Pedir e consultar cotações
Anterior
Decidir uma cotação
Próxima
Proposta por link (sem login)
Nesta página
  • Fazer contraproposta
  • Ver o histórico da negociação
  • Aceitar uma rodada
  • Recusar uma rodada
  • Levar ao carrinho
  • Veja também