Proposta por link (sem login)
Abrir, baixar em PDF, aprovar, recusar e comentar uma proposta usando só o token do link.
Quem aprova uma compra B2B nem sempre tem conta na loja. A proposta por link resolve isso: a loja gera um link com um token secreto para uma cotação respondida, o cliente recebe por e-mail e qualquer pessoa com o link pode abrir, baixar o PDF e decidir. Estas cinco rotas são públicas; o token é o segredo, então trate-o como uma senha.
Abrir a proposta
Uma proposta é o link formal que a loja gera para uma cotação respondida (quoted) e envia ao cliente por e-mail, para que quem decide a compra aprove sem ter conta. Esta rota devolve a cotação e o estado do link. Cada abertura é registrada na trilha de auditoria (no máximo uma a cada 5 minutos).
/api/v1/storefront/quote-proposals/{token}PúblicaMostra a proposta a quem tem o link, sem login
Parâmetros de caminho
tokenstringobrigatórioExemplo de requisição
curl "$BASE_URL/api/v1/storefront/quote-proposals/Zk3pQ8vYh2LmN5xW7cR1tB9aD4eF6gHj"Resposta
{
"status": "active",
"quote": {
"id": 42,
"user_id": "e0d4f8a1-5b2c-4d7e-9a31-6c8f0b2d4e6a",
"vendor_id": "7c1e5d3a-2b4f-4a6e-9c8d-1f0a3b5c7d9e",
"status": "quoted",
"notes": "Preciso de entrega mensal",
"total_cents": 420000,
"created_at": "2026-10-11T14:02:11Z",
"updated_at": "2026-10-11T14:02:11Z",
"user_email": "maria.souza@example.com",
"user_name": "Maria Souza",
"vendor_name": "Distribuidora Aurora",
"items": [
{
"id": 101,
"quote_id": 42,
"product_id": "c3f92551-9dda-4fc7-b518-353d1c4e4035",
"quantity": 200,
"requested_price_cents": 1800,
"notes": "",
"product_name": "Caneta esferográfica azul (cx 50)",
"product_sku": "CAN-AZ-50",
"product_price": 2490,
"offered_price_cents": 2100
}
],
"vendor_notes": "Preço válido para pedido único.",
"valid_until": "2026-10-25T23:59:59Z"
},
"expires_at": "2026-10-25T23:59:59Z",
"has_pdf": true
}Campos da resposta
statusstringactiveexpiredrevokeddecidedactive aceita aprovar ou recusar.quoteobjectexpires_atstringvalid_until da cotação ou 30 dias após a geração.has_pdfbooleantrue quando há PDF para baixar em Baixar o PDF.Erros
| Status | Quando | Corpo de exemplo |
|---|---|---|
| 404 | Token desconhecido. | {"error":"proposal not found"} |
Baixar o PDF
Devolve o PDF da proposta (application/pdf, Content-Disposition: inline; filename="proposta.pdf"), gerado quando o link foi criado. A resposta traz Cache-Control: private, no-store: não a guarde em cache compartilhado.
/api/v1/storefront/quote-proposals/{token}/pdfPúblicaPDF da proposta
Parâmetros de caminho
tokenstringobrigatórioExemplo de requisição
curl "$BASE_URL/api/v1/storefront/quote-proposals/Zk3pQ8vYh2LmN5xW7cR1tB9aD4eF6gHj/pdf"Resposta
%PDF-1.4 ... (bytes)Erros
| Status | Quando | Corpo de exemplo |
|---|---|---|
| 404 | Token desconhecido ou PDF indisponível. | {"error":"proposal not found"} |
| 410 | Link revogado, expirado ou já decidido. | {"error":"proposal link is no longer active"} |
Aprovar a proposta
Registra a aprovação: o link vira decided e a cotação passa de quoted para accepted. Em seguida a plataforma tenta colocar os itens no carrinho do dono da cotação com os preços oferecidos. Se essa conversão falhar (por exemplo, falta de estoque), a aprovação continua valendo e a loja é avisada por evento. Nome, cargo, IP e navegador de quem aprovou são gravados.
/api/v1/storefront/quote-proposals/{token}/approvePúblicaAprova a cotação pelo link, sem login
Parâmetros de caminho
tokenstringobrigatórioCorpo da requisição
namestringobrigatóriorolestringcommentstringExemplo de requisição
curl -X POST "$BASE_URL/api/v1/storefront/quote-proposals/Zk3pQ8vYh2LmN5xW7cR1tB9aD4eF6gHj/approve" \
-H "Content-Type: application/json" \
-d '{
"name": "Carlos Pereira",
"role": "Diretor de compras",
"comment": "De acordo."
}'Resposta
{
"action": "approve",
"status": "recorded"
}Erros
| Status | Quando | Corpo de exemplo |
|---|---|---|
| 400 | name vazio. | {"error":"name is required"} |
| 404 | Token desconhecido. | {"error":"proposal not found"} |
| 409 | Outra pessoa decidiu primeiro (corrida) ou a cotação já saiu de quoted. | {"error":"proposal already decided"} |
| 410 | Link revogado, expirado ou já decidido. | {"error":"proposal link is no longer active"} |
Recusar a proposta
Registra a recusa: o link vira decided e a cotação passa a rejected. Mesmo corpo e mesma trilha de auditoria da aprovação.
/api/v1/storefront/quote-proposals/{token}/rejectPúblicaRecusa a cotação pelo link, sem login
Parâmetros de caminho
tokenstringobrigatórioCorpo da requisição
namestringobrigatóriorolestringcommentstringExemplo de requisição
curl -X POST "$BASE_URL/api/v1/storefront/quote-proposals/Zk3pQ8vYh2LmN5xW7cR1tB9aD4eF6gHj/reject" \
-H "Content-Type: application/json" \
-d '{
"name": "Carlos Pereira",
"role": "Diretor de compras",
"comment": "Fora do orçamento deste trimestre."
}'Resposta
{
"action": "reject",
"status": "recorded"
}Erros
| Status | Quando | Corpo de exemplo |
|---|---|---|
| 400 | name vazio. | {"error":"name is required"} |
| 404 | Token desconhecido. | {"error":"proposal not found"} |
| 409 | Já decidida. | {"error":"proposal already decided"} |
| 410 | Link revogado, expirado ou já decidido. | {"error":"proposal link is no longer active"} |
Comentar a proposta
Registra um comentário na trilha de auditoria sem aprovar nem recusar; o seller o enxerga no painel. Aqui o name é opcional. É a única ação permitida num link já expirado: serve para o botão "pedir proposta atualizada". Em link revogado ou já decidido, responde 410.
/api/v1/storefront/quote-proposals/{token}/commentPúblicaDeixa um comentário para o seller sem decidir
Parâmetros de caminho
tokenstringobrigatórioCorpo da requisição
namestringrolestringcommentstringExemplo de requisição
curl -X POST "$BASE_URL/api/v1/storefront/quote-proposals/Zk3pQ8vYh2LmN5xW7cR1tB9aD4eF6gHj/comment" \
-H "Content-Type: application/json" \
-d '{
"name": "Carlos Pereira",
"comment": "Podem enviar uma proposta atualizada?"
}'Resposta
{
"action": "comment",
"status": "recorded"
}Erros
| Status | Quando | Corpo de exemplo |
|---|---|---|
| 404 | Token desconhecido. | {"error":"proposal not found"} |
| 410 | Link revogado, expirado ou já decidido. | {"error":"proposal link is no longer active"} |