> For the complete documentation index, see [llms.txt](https://help.dollarlabs.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://help.dollarlabs.io/dollarlabs-b2b-custom-pricing/developer-tools/quotes-api.md).

# Quotes API

Read quotes, their negotiation history and the buyer's form answers from your own systems.

Read [quotes](/dollarlabs-b2b-custom-pricing/quotes.md) from your ERP, CRM or reporting tools. The Quotes API is read-only: create offers and accept or decline quotes in the app.

It uses the same [API keys](/dollarlabs-b2b-custom-pricing/developer-tools/api-keys.md) and base URL as the [Pricing API](/dollarlabs-b2b-custom-pricing/developer-tools/api-for-bulk-update-pricing.md). A key you already use for pricing can read quotes too. Quotes must be available on your plan; otherwise these endpoints return `404`.

```
https://pricelist-app.dollarlabs.io/api/v1/
```

Every request sends both headers:

```bash
curl "https://pricelist-app.dollarlabs.io/api/v1/quotes?status=active" \
  -H "X-API-Key: dlb_your_api_key" \
  -H "X-Signing-Secret: sec_your_signing_secret"
```

## List quotes

`GET /api/v1/quotes`

| Parameter       | Default | Description                                                                                                            |
| --------------- | ------- | ---------------------------------------------------------------------------------------------------------------------- |
| `status`        | —       | A status (below), or a group: `active` (waiting on either side), `agreed` (accepted) or `closed` (declined or expired) |
| `updated_since` | —       | ISO timestamp. Only quotes updated at or after this time                                                               |
| `limit`         | `100`   | Page size, up to `500`                                                                                                 |
| `cursor`        | —       | `pageInfo.nextCursor` from the previous page                                                                           |

```json
{
  "quotes": [
    {
      "id": "qt_07b624b9-877",
      "status": "MERCHANT_PROPOSAL",
      "ownerType": "CUSTOMER",
      "ownerGid": "gid://shopify/Customer/123456789",
      "currency": "USD",
      "currentVersion": 2,
      "total": "2025.00",
      "draftOrderId": "gid://shopify/DraftOrder/1110642753602",
      "orderId": null,
      "expiresAt": "2026-11-06T04:59:59.999Z",
      "createdAt": "2026-10-22T12:44:52.000Z",
      "updatedAt": "2026-10-22T12:47:46.000Z"
    }
  ],
  "pageInfo": { "limit": 100, "nextCursor": null }
}
```

* `total` is quantity × price across the current version's lines. A line without a price counts as `0`.
* `draftOrderId`, `orderId` and `expiresAt` are `null` when not set.
* Results are ordered by `updatedAt`, oldest first. Keep requesting with `cursor` until `nextCursor` is `null`.

### Statuses

| Status                                | In the app                       |
| ------------------------------------- | -------------------------------- |
| `CUSTOMER_PROPOSAL`                   | Needs your response              |
| `MERCHANT_PROPOSAL`                   | Sent to customer                 |
| `CUSTOMER_ACCEPT` / `MERCHANT_ACCEPT` | Customer accepted / You accepted |
| `CUSTOMER_REJECT` / `MERCHANT_REJECT` | Customer declined / You declined |
| `EXPIRED`                             | Expired                          |

## Get one quote

`GET /api/v1/quotes/{id}`

Returns the same fields, plus:

* `versions` — every offer, oldest first: `version`, `actor` (`CUSTOMER` or `MERCHANT`), `lines` (`variantId`, `qty`, `price`, `listPrice`), `note`, `createdAt`
* `formData` — the buyer's request form answers as `label` / `value` pairs
* `events` — `created`, `countered`, `accepted`, `declined`, `expired` and `ordered`, with the version and time

```json
{
  "quote": {
    "id": "qt_07b624b9-877",
    "status": "MERCHANT_PROPOSAL",
    "currentVersion": 2,
    "versions": [
      {
        "version": 1,
        "actor": "CUSTOMER",
        "lines": [
          { "variantId": "gid://shopify/ProductVariant/45234710741058", "qty": 500, "price": "1.10", "listPrice": "2.00" },
          { "variantId": "gid://shopify/ProductVariant/45234710610000", "qty": 200, "listPrice": "10.00" }
        ],
        "note": null,
        "createdAt": "2026-10-22T12:44:52.000Z"
      },
      {
        "version": 2,
        "actor": "MERCHANT",
        "lines": [
          { "variantId": "gid://shopify/ProductVariant/45234710741058", "qty": 500, "price": "1.25", "listPrice": "2.00" },
          { "variantId": "gid://shopify/ProductVariant/45234710610000", "qty": 200, "price": "7.00", "listPrice": "10.00" }
        ],
        "note": "Price holds to March.",
        "createdAt": "2026-10-22T12:47:46.000Z"
      }
    ],
    "formData": [{ "label": "Needed by", "value": "2026-11-20" }],
    "events": [
      { "type": "created", "version": 1, "createdAt": "2026-10-22T12:44:52.000Z" },
      { "type": "countered", "version": 2, "createdAt": "2026-10-22T12:47:46.000Z" }
    ]
  }
}
```

{% hint style="warning" %}
`formData` holds whatever the buyer typed into your request form — names, phone numbers, notes. Store and share it with the same care as customer data.
{% endhint %}

## Errors and limits

| Status | Body                                                  | Cause                                                                            |
| ------ | ----------------------------------------------------- | -------------------------------------------------------------------------------- |
| `400`  | `invalid_status`, `invalid_limit` or `invalid_cursor` | A query parameter isn't valid                                                    |
| `401`  | An error message                                      | Missing or wrong `X-API-Key` / `X-Signing-Secret`                                |
| `403`  | `IP not allowed for this API key`                     | The key is limited to other IP addresses                                         |
| `404`  | `quote_not_found` / `Not found`                       | No quote with that ID, or Quotes isn't on your plan                              |
| `405`  | `Method not allowed`                                  | The API is read-only; use `GET`                                                  |
| `429`  | `Rate limit exceeded…` with `retryAfter`              | More than 100 requests a minute for this key. Wait for the `Retry-After` seconds |
