---
title: "Post messages and send proposals"
description: "Post messages to the buyer and send a proposal your team already priced, over the QuotWay API - staged proposals, price floors and approvals."
url: "https://www.quotway.com/ja/docs/api/messages-and-proposals"
type: "documentation"
category: "api"
updated: "2026-09-30"
locale: "ja"
source: "QuotWay - B2B Quote & Negotiation App for Shopify"
---

# Post messages and send proposals

**Read time:** 6 minutes.
**Who it's for:** Developers connecting a CRM, ERP or support tool that needs to talk to buyers
through QuotWay, or to release a proposal once an outside process signs it off.

> ⚠️ Both endpoints need the **Enterprise** plan and a key with the `write_quotes` scope. Send an
> [`Idempotency-Key`](/docs/api/idempotency) on every call.

## Post a message - `POST /v1/quotes/{id}/messages`

Adds a message from the store to the quote's conversation.

```bash
curl https://api.quotway.com/v1/quotes/clz8k2x9f0001qw7h3m4n5p6r/messages \
  -H "Authorization: Bearer $QUOTWAY_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 3a9c2d1e-7f6b-4a5c-9d8e-1f2a3b4c5d6e" \
  -d '{"body": "Good news — we can do the 12 oz lids in the same shipment."}'
```

| Field | Required | Description |
|---|---|---|
| `body` | Yes | The message text, 1–5,000 characters (leading and trailing whitespace is trimmed). |
| `internal` | No | `true` makes it an **internal note**: visible to your team in QuotWay only. Default `false`. |

Response - `201 Created`:

```json
{
  "object": "message",
  "id": "clz9c4d5e0007qw7hb8c9d0e1",
  "quote_id": "clz8k2x9f0001qw7h3m4n5p6r",
  "internal": false,
  "body": "Good news — we can do the 12 oz lids in the same shipment.",
  "created_at": "2026-09-29T15:04:11.820Z"
}
```

What happens next:

- **A buyer-facing message** appears in the quote's conversation, is sent under your store's name,
  and is **emailed to the buyer**. It fires the `quote.message_created` webhook. (If the email can't
  be queued, the message is still recorded.)
- **An internal note** (`"internal": true`) is **never emailed** and **never** published to
  webhooks, the events feed or any integration.
- Buyer-facing messages count toward the store's
  [daily buyer-email allowance](/docs/api/rate-limits#is-there-a-limit-on-emails-to-buyers); internal
  notes don't. Over the allowance you get `429 email_quota_exceeded`.

The API can post messages; it can't read the conversation back.

## Send the staged proposal - `POST /v1/quotes/{id}/send-proposal`

Sends the proposal your team **already priced and saved** in QuotWay's proposal editor. It's the
same action as pressing **Send proposal** in the admin, with the same checks. **The API never sets
prices** - if nobody has saved a proposal draft, there's nothing to send.

Typical uses: an ERP or CPQ approves margins before release; a CRM step marks the deal ready; a
batch job releases proposals at the start of the business day.

```bash
curl -X POST https://api.quotway.com/v1/quotes/clz8k2x9f0001qw7h3m4n5p6r/send-proposal \
  -H "Authorization: Bearer $QUOTWAY_API_KEY" \
  -H "Idempotency-Key: 5e4d3c2b-1a09-4f8e-b7d6-c5b4a3928170"
```

The body is optional. Its only field:

| Field | Description |
|---|---|
| `confirm_below_floor` | `true` sends even if some lines are below the store's price floor - the same explicit confirmation the admin asks for. |

### What gets checked

In order, exactly as in the admin:

1. **A saved draft exists** - otherwise `409 no_staged_proposal`.
2. **The quote can take a proposal** - its status must be `submitted`, `in_review` or `countered`;
   otherwise `409 not_editable`.
3. **B2B catalog prices** - for a `b2b` quote, the company location's catalog prices are re-read from
   Shopify as the baseline.
4. **Price floor** - any line below the floor stops the send with `409 below_price_floor` (listing
   the lines), unless you pass `confirm_below_floor: true`. Each line is judged at the price the
   buyer pays, with its share of any whole-quote discount taken off. A fixed-amount discount larger
   than the subtotal is refused first, with `422 invalid_proposal` - `confirm_below_floor` doesn't
   override it.
5. **The proposal is valid** - e.g. every line has a price; otherwise `422 invalid_proposal`.
6. **Approval policies** - if a policy requires sign-off (say, discounts over 10%), the proposal is
   parked for your team's approval instead of going to the buyer.
7. **Sent** - the buyer is emailed, with the Proposal PDF when the store generates one automatically.

### Response - `200 OK`

```json
{
  "object": "proposal_send",
  "outcome": "sent",
  "version_number": 2,
  "quote": { "object": "quote", "id": "clz8k2x9f0001qw7h3m4n5p6r", "status": "proposal_sent", "…": "…" }
}
```

| `outcome` | Meaning |
|---|---|
| `sent` | The proposal went to the buyer. The quote is `proposal_sent`; `quote.proposal_sent` fires. |
| `awaiting_approval` | An approval policy parked it. The quote is `awaiting_merchant_approval`; `quote.merchant_approval.requested` fires, and the buyer is notified once it's approved. |

`quote` is the full [quote object](/docs/api/quote-object) after the send.

### Errors

| Status | Code | What to do |
|---|---|---|
| 409 | [`no_staged_proposal`](/docs/api/errors#no_staged_proposal) | Price and save the proposal in QuotWay first. |
| 409 | [`not_editable`](/docs/api/errors#not_editable) | Check the quote's status. |
| 409 | [`below_price_floor`](/docs/api/errors#below_price_floor) | Review prices, or resend with `confirm_below_floor: true`. |
| 422 | [`invalid_proposal`](/docs/api/errors#invalid_proposal) | Fix the draft in QuotWay. |
| 409 | [`state_conflict`](/docs/api/errors#state_conflict) | The quote changed mid-send. Re-fetch, then retry with a new idempotency key. |
| 409 | [`send_incomplete`](/docs/api/errors#send_incomplete) | A version was created but the send didn't finish. Check the quote before doing anything else. |
| 404 | [`not_found`](/docs/api/errors#not_found) | No such quote in this store. |
| 429 | [`email_quota_exceeded`](/docs/api/errors#email_quota_exceeded) | The store's daily allowance of buyer-emailing API calls is used up. |

Every refusal is a `4xx`, and `4xx` responses are stored against your `Idempotency-Key`, so a
retry with the same key replays the refusal rather than creating a second proposal version.

## What the API can't do (yet)

There are no endpoints to price or edit lines, change a proposal, accept or decline on the buyer's
behalf, or convert a quote to a draft order. Those stay in QuotWay, where your team, your price
floor and your approval policies govern them.

## Related

- [Floor price and discounts](/docs/negotiation/floor-price-and-discounts)
- [Approval workflows](/docs/approvals/approval-workflows)
- [Messaging with buyers](/docs/negotiation/messaging-with-buyers)
