Negociação em rodadas
Contrapropostas, histórico e aceite ou recusa de rodadas de uma cotação B2B.
Quando o preço não fecha de primeira, comprador e seller trocam ofertas em rodadas. Cada rodada é uma proposta completa (linhas, quantidades e preços). Esta página cobre o lado do seller. As quatro rotas exigem o módulo enhanced-rfq ligado, além de b2b-pricing.
Enviar contraproposta
Abre uma nova rodada com as linhas, preços e quantidades que o seller propõe. A rodada anterior ainda pending vira countered, o total da cotação passa a ser o da nova rodada e o status da cotação vira negotiating. O limite é de 5 rodadas por cotação (max_rounds).
/api/v1/vendor/quotes/{id}/counterSellerNova rodada de negociação
Cabeçalhos
AuthorizationstringobrigatórioBearer $TOKEN. Token de acesso de um usuário seller (veja Tokens).Content-Typestringobrigatórioapplication/json.Parâmetros de caminho
idintegerobrigatório400 invalid quote id.Corpo da requisição
itemsobject[]obrigatórioLinhas da proposta. Pelo menos uma.
product_iduuidobrigatórioquantityintegerobrigatórioprice_centsintegerobrigatórionotesstringmessagestringexpires_atstringExemplo de requisição
curl -X POST "$BASE_URL/api/v1/vendor/quotes/69/counter" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"items": [
{
"product_id": "c7bc3778-2c79-4561-b9d7-979768f364c8",
"quantity": 38,
"price_cents": 4200
}
],
"message": "Consigo 38 unidades a R$ 42,00."
}'Resposta
{
"id": "08199264-d0cf-4eb9-a26d-7216f7c450ba",
"quote_id": 69,
"round_number": 2,
"initiated_by": "vendor",
"message": "Consigo 38 unidades a R$ 42,00.",
"items": [
{
"product_id": "c7bc3778-2c79-4561-b9d7-979768f364c8",
"product_name": "Notebook Plus Rosa",
"quantity": 38,
"price_cents": 4200
}
],
"total_cents": 159600,
"status": "pending",
"created_at": "2026-10-11T15:00:00Z"
}Campos da resposta
iduuidroundId das rotas de aceitar e recusar.quote_idintegerround_numberintegerinitiated_bystringcustomervendoradminmessagestringitemsobject[]product_id, product_name, quantity, price_cents (unitário, centavos) e notes.total_centsintegerstatusstringpendingacceptedrejectedcounteredexpiredexpires_atstring|nullcreated_atstringErros
| Status | Quando | Corpo de exemplo |
|---|---|---|
| 400 | Falha de regra, com o motivo em detail: cotação fora de um estado negociável (pending, quoted ou negotiating), limite de rodadas, lista de itens vazia, quantidade menor que 1 ou preço negativo. | {"error":"counter-offer failed","detail":"maximum negotiation rounds (5) exceeded"} |
| 404 | A cotação não existe. | {"error":"quote not found"} |
| 403 | A cotação é de outro seller. | {"error":"quote does not belong to this vendor"} |
| 401 | Token ausente, inválido, expirado ou revogado. | {"error":"missing authorization header"} |
| 403 | O token não é de um usuário seller (por exemplo, é de administrador), ou o usuário não está vinculado a nenhum seller. | {"error":"insufficient permissions"} |
| 403 | O módulo b2b-pricing está desligado nesta loja. | {"error":"module_disabled","module":"b2b-pricing"} |
| 403 | O módulo enhanced-rfq está desligado nesta loja. | {"error":"module_disabled","module":"enhanced-rfq"} |
Histórico da negociação
Devolve o limite de rodadas, a rodada atual e todas as rodadas da cotação em ordem. Use para saber qual rodada está pending e pode ser aceita.
/api/v1/vendor/quotes/{id}/negotiationsSellerTodas as rodadas da cotação
Cabeçalhos
AuthorizationstringobrigatórioBearer $TOKEN. Token de acesso de um usuário seller (veja Tokens).Parâmetros de caminho
idintegerobrigatório400 invalid quote id.Exemplo de requisição
curl "$BASE_URL/api/v1/vendor/quotes/69/negotiations" \
-H "Authorization: Bearer $TOKEN"Resposta
{
"quote_id": 69,
"max_rounds": 5,
"current_round": 1,
"rounds": [
{
"id": "08199264-d0cf-4eb9-a26d-7216f7c450ba",
"quote_id": 69,
"round_number": 1,
"initiated_by": "customer",
"message": "",
"items": [
{
"product_id": "c7bc3778-2c79-4561-b9d7-979768f364c8",
"product_name": "Notebook Plus Rosa",
"quantity": 38,
"price_cents": 4000
}
],
"total_cents": 152000,
"status": "pending",
"created_at": "2026-10-11T15:00:00Z"
}
]
}Campos da resposta
quote_idintegermax_roundsintegercurrent_roundinteger0 se ninguém negociou.expires_atstring|nullroundsobject[]Rodadas, da primeira para a última.
iduuidroundId das rotas de aceitar e recusar.quote_idintegerround_numberintegerinitiated_bystringcustomervendoradminmessagestringitemsobject[]product_id, product_name, quantity, price_cents (unitário, centavos) e notes.total_centsintegerstatusstringpendingacceptedrejectedcounteredexpiredexpires_atstring|nullcreated_atstringErros
| Status | Quando | Corpo de exemplo |
|---|---|---|
| 400 | id não é um inteiro. | {"error":"invalid quote id"} |
| 404 | A cotação não existe. | {"error":"quote not found"} |
| 403 | A cotação é de outro seller. | {"error":"quote does not belong to this vendor"} |
| 401 | Token ausente, inválido, expirado ou revogado. | {"error":"missing authorization header"} |
| 403 | O token não é de um usuário seller (por exemplo, é de administrador), ou o usuário não está vinculado a nenhum seller. | {"error":"insufficient permissions"} |
| 403 | O módulo b2b-pricing está desligado nesta loja. | {"error":"module_disabled","module":"b2b-pricing"} |
| 403 | O módulo enhanced-rfq está desligado nesta loja. | {"error":"module_disabled","module":"enhanced-rfq"} |
Aceitar uma rodada
Aceita uma rodada pending: as outras rodadas pendentes viram rejected, as linhas da cotação passam a ter os preços e quantidades da rodada (linhas que ficaram de fora são apagadas, produtos novos entram) e a cotação vira accepted com o total da rodada. A plataforma não confere quem fez a oferta: o seller consegue aceitar até uma rodada criada por ele mesmo.
/api/v1/vendor/quotes/{id}/accept-round/{roundId}SellerAceitar a oferta de uma rodada
Cabeçalhos
AuthorizationstringobrigatórioBearer $TOKEN. Token de acesso de um usuário seller (veja Tokens).Parâmetros de caminho
idintegerobrigatório400 invalid quote id.roundIduuidobrigatórioid de uma rodada de negotiations).Exemplo de requisição
curl -X POST "$BASE_URL/api/v1/vendor/quotes/69/accept-round/08199264-d0cf-4eb9-a26d-7216f7c450ba" \
-H "Authorization: Bearer $TOKEN"Resposta
{
"message": "round accepted"
}Erros
| Status | Quando | Corpo de exemplo |
|---|---|---|
| 400 | Rodada inexistente na cotação ou que não está pending. O motivo vem em detail. | {"error":"failed to accept round","detail":"round is not in pending status (current: A)"} |
| 422 | A trava de margem da loja bloqueou: algum preço da rodada fica abaixo do piso de margem. O corpo é o veredito da trava. | {"ok":false,"floor_bps":1500,"reason":"below_floor"} |
| 404 | A cotação não existe. | {"error":"quote not found"} |
| 403 | A cotação é de outro seller. | {"error":"quote does not belong to this vendor"} |
| 401 | Token ausente, inválido, expirado ou revogado. | {"error":"missing authorization header"} |
| 403 | O token não é de um usuário seller (por exemplo, é de administrador), ou o usuário não está vinculado a nenhum seller. | {"error":"insufficient permissions"} |
| 403 | O módulo b2b-pricing está desligado nesta loja. | {"error":"module_disabled","module":"b2b-pricing"} |
| 403 | O módulo enhanced-rfq está desligado nesta loja. | {"error":"module_disabled","module":"enhanced-rfq"} |
Recusar uma rodada
Marca a rodada pending como rejected. O motivo é opcional e é anexado à mensagem da rodada. Diferente de recusar a cotação, a negociação continua possível: você ou o comprador podem abrir outra rodada.
/api/v1/vendor/quotes/{id}/reject-round/{roundId}SellerRecusar a oferta de uma rodada
Cabeçalhos
AuthorizationstringobrigatórioBearer $TOKEN. Token de acesso de um usuário seller (veja Tokens).Content-Typestringobrigatórioapplication/json.Parâmetros de caminho
idintegerobrigatório400 invalid quote id.roundIduuidobrigatórioid de uma rodada de negotiations).Corpo da requisição
reasonstringExemplo de requisição
curl -X POST "$BASE_URL/api/v1/vendor/quotes/69/reject-round/08199264-d0cf-4eb9-a26d-7216f7c450ba" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"reason": "Abaixo do nosso custo."
}'Resposta
{
"message": "round rejected"
}Erros
| Status | Quando | Corpo de exemplo |
|---|---|---|
| 400 | Rodada inexistente na cotação ou que não está pending. | {"error":"failed to reject round","detail":"negotiation round not found"} |
| 404 | A cotação não existe. | {"error":"quote not found"} |
| 403 | A cotação é de outro seller. | {"error":"quote does not belong to this vendor"} |
| 401 | Token ausente, inválido, expirado ou revogado. | {"error":"missing authorization header"} |
| 403 | O token não é de um usuário seller (por exemplo, é de administrador), ou o usuário não está vinculado a nenhum seller. | {"error":"insufficient permissions"} |
| 403 | O módulo b2b-pricing está desligado nesta loja. | {"error":"module_disabled","module":"b2b-pricing"} |
| 403 | O módulo enhanced-rfq está desligado nesta loja. | {"error":"module_disabled","module":"enhanced-rfq"} |