# Quotes

A quote is our offer for a sourcing request: a price for each product and shipping options to your destination. When we change a quote we send a new version: it has a new `id`, the same `number` and a higher `version`, and the old version becomes `SUPERSEDED` with `newerVersionId` pointing to the new one. The list shows the version you can see of each quote number, newest quote number first.

Webhook `quote.created` tells you about every new quote and every new version. Accept or decline quotes in the portal; the API can't do that yet. Accepting writes the agreed prices into My products (the products then show `price.kind` `AGREED` and can be restocked) and moves the request on to `DONE`; a purchase order can follow. Declining the latest version sends the request back to `IN_PROGRESS`. There is no `updatedSince` here: use webhooks and GET, or read the first pages again to find new ones (the list is newest first). See [Incremental sync](https://docs.rephut.com/developers/incremental-sync).

## Quote statuses

| Value | Meaning |
|---|---|
| `AWAITING` | Awaiting your decision. Accept or decline it in the portal. |
| `UPDATING` | We are preparing a new version. |
| `SUPERSEDED` | Replaced by a newer version; `newerVersionId` points to it. |
| `ACCEPTED` | You accepted it. |
| `DECLINED` | You declined it. |
| `EXPIRED` | `validUntil` passed without a decision. |

## List quotes

`GET /v1/quotes`

The version you can see of each quote number. Newest quote number first.

| Property | Value |
|---|---|
| URL | `https://api.rephut.com/v1/quotes` |
| Permission | `quotes:read` |
| Access | Read: counts towards the read limit and works while your account is on hold |
| Idempotency-Key | Not used |
| Success | 200 OK |

### Query parameters

| Field | Type | Required | Description |
|---|---|---|---|
| `page` | integer | No | Page number. See [Pagination](https://docs.rephut.com/developers/pagination). From 1 to 10,000. Default: `1`. |

### Headers

| Header | Required | Value |
|---|---|---|
| `Authorization` | Yes | `Bearer <your API key>`. See [Authentication](https://docs.rephut.com/developers/authentication). |

### Response

`200` with `data`, a list of objects with the fields below (up to one page), and `meta` with `total`, `page`, `limit` and `pageCount`. See [Pagination](https://docs.rephut.com/developers/pagination).

| Field | Type | Description |
|---|---|---|
| `id` | string | Quote id. Each version of a quote has its own id. |
| `number` | string | Our quote number; the same for every version. |
| `version` | integer | Version number, starting at 1. |
| `status` | string | Quote status. See [Quote statuses](#quote-statuses). One of `AWAITING`, `UPDATING`, `SUPERSEDED`, `ACCEPTED`, `DECLINED`, `EXPIRED`. |
| `statusLabel` | string | English text for display. It may change; rely on the code next to it. |
| `issuedAt` | string (date-time) | When we sent this version. |
| `validUntil` | string (date-time) | The prices are valid until this time. |
| `itemCount` | integer | Number of products. |
| `currency` | string | Currency of every amount. Always `USD`. |
| `goodsCents` | integer | Total for the goods in US cents, without shipping. |

### Errors

| Code | HTTP status | Meaning |
|---|---|---|
| `invalid_api_key` | 401 | The API key is missing, malformed, revoked or no longer valid. |
| `account_action_required` | 403 | Sign in to the portal and finish the required account steps first. |
| `api_not_enabled` | 403 | API access is not enabled for this account. |
| `insufficient_scope` | 403 | The API key does not have the permission this call needs. |
| `validation_failed` | 422 | The input is not valid. `details` lists each field and the reason. |
| `rate_limited` | 429 | Too many requests. Wait for the number of seconds in Retry-After. |
| `internal_error` | 500 | Unexpected error. Quote X-Request-Id when you contact support. |

Handle errors by `code`. See [Errors](https://docs.rephut.com/developers/errors).

```bash example=curl
curl 'https://api.rephut.com/v1/quotes?page=1' \
  -H "Authorization: Bearer $FULFILLBOT_API_KEY"
```

```js example=node
const response = await fetch("https://api.rephut.com/v1/quotes?page=1", {
  headers: {
    Authorization: `Bearer ${process.env.FULFILLBOT_API_KEY}`,
  },
});
const result = await response.json();
console.log(response.status, result);
```

```python example=python
import os

import requests

response = requests.get(
    "https://api.rephut.com/v1/quotes?page=1",
    headers={
        "Authorization": f"Bearer {os.environ['FULFILLBOT_API_KEY']}",
    },
    timeout=30,
)
print(response.status_code, response.json())
```

```json example=response status=200
{
  "success": true,
  "data": [
    {
      "id": "cm3u2qt0e0001quo6h7j8k9l",
      "number": "Q2611100001",
      "version": 1,
      "status": "AWAITING",
      "statusLabel": "Awaiting your decision",
      "issuedAt": "2026-11-10T07:00:00.000Z",
      "validUntil": "2026-11-17T07:00:00.000Z",
      "itemCount": 1,
      "currency": "USD",
      "goodsCents": 72000
    }
  ],
  "meta": {
    "total": 1,
    "page": 1,
    "limit": 50,
    "pageCount": 1
  }
}
```

## Get a quote

`GET /v1/quotes/{id}`

| Property | Value |
|---|---|
| URL | `https://api.rephut.com/v1/quotes/{id}` |
| Permission | `quotes:read` |
| Access | Read: counts towards the read limit and works while your account is on hold |
| Idempotency-Key | Not used |
| Success | 200 OK |

### Path parameters

| Name | Type | Description |
|---|---|---|
| `id` | string | Quote id, from [`GET /v1/quotes`](https://docs.rephut.com/developers/reference/quotes#list-quotes) or `quoteId` of a request. An id that doesn't exist, isn't yours or isn't a valid id returns 404 `not_found`. |

### Headers

| Header | Required | Value |
|---|---|---|
| `Authorization` | Yes | `Bearer <your API key>`. See [Authentication](https://docs.rephut.com/developers/authentication). |

### Response

`200` with `data`, an object with the fields below.

| Field | Type | Description |
|---|---|---|
| `id` | string | Quote id. Each version of a quote has its own id. |
| `number` | string | Our quote number; the same for every version. |
| `version` | integer | Version number, starting at 1. |
| `status` | string | Quote status. See [Quote statuses](#quote-statuses). One of `AWAITING`, `UPDATING`, `SUPERSEDED`, `ACCEPTED`, `DECLINED`, `EXPIRED`. |
| `statusLabel` | string | English text for display. It may change; rely on the code next to it. |
| `canDecide` | boolean | `true` while you can accept or decline it. Accept or decline quotes in the portal; the API can't do it yet. |
| `issuedAt` | string (date-time) | When we sent this version. |
| `validUntil` | string (date-time) | The prices are valid until this time. |
| `decidedAt` | string (date-time) or null | When it was accepted or declined (by you or by our team for you); null before that. |
| `destinationCountryCode` | string | Two-letter destination country code. |
| `destination` | string | Destination in English. |
| `paymentMethodName` | string or null | Payment method for this quote; null when none. |
| `note` | string | Our note. |
| `customerFeedback` | string | Feedback you sent us about this quote; empty when none. |
| `currency` | string | Currency of every amount. Always `USD`. |
| `items` | array of objects | Quoted products. |
| `items[].sku` | string | Our SKU. |
| `items[].name` | string | Product name in English. |
| `items[].attributes` | string | Variant details in English. |
| `items[].customerSku` | string | Your SKU for the product; empty when none. |
| `items[].qty` | integer | Quantity. |
| `items[].unitPriceCents` | integer | Unit price in US cents. |
| `items[].amountCents` | integer | Line amount in US cents. |
| `goodsCents` | integer | Total for the goods in US cents, without shipping. |
| `shippingOptions` | array of objects | Shipping options we quoted. |
| `shippingOptions[].label` | string | Name of the option. |
| `shippingOptions[].transitDays` | string | Delivery time, for example 7-12 days. |
| `shippingOptions[].shippingCents` | integer | Shipping in US cents. |
| `shippingOptions[].totalCents` | integer | Goods plus shipping, in US cents. |
| `shippingOptions[].shippingMaxCents` | integer or null | Upper bound when shipping depends on the postcode; otherwise null. |
| `shippingOptions[].totalMaxCents` | integer or null | Upper bound of the total when shipping depends on the postal code; otherwise null. |
| `newerVersionId` | string or null | Set when a newer version replaced this one. |

### Errors

| Code | HTTP status | Meaning |
|---|---|---|
| `invalid_api_key` | 401 | The API key is missing, malformed, revoked or no longer valid. |
| `account_action_required` | 403 | Sign in to the portal and finish the required account steps first. |
| `api_not_enabled` | 403 | API access is not enabled for this account. |
| `insufficient_scope` | 403 | The API key does not have the permission this call needs. |
| `not_found` | 404 | The resource does not exist or does not belong to this account. |
| `validation_failed` | 422 | The input is not valid. `details` lists each field and the reason. |
| `rate_limited` | 429 | Too many requests. Wait for the number of seconds in Retry-After. |
| `internal_error` | 500 | Unexpected error. Quote X-Request-Id when you contact support. |

Handle errors by `code`. See [Errors](https://docs.rephut.com/developers/errors).

```bash example=curl
curl 'https://api.rephut.com/v1/quotes/cm3u2qt0e0001quo6h7j8k9l' \
  -H "Authorization: Bearer $FULFILLBOT_API_KEY"
```

```js example=node
const response = await fetch("https://api.rephut.com/v1/quotes/cm3u2qt0e0001quo6h7j8k9l", {
  headers: {
    Authorization: `Bearer ${process.env.FULFILLBOT_API_KEY}`,
  },
});
const result = await response.json();
console.log(response.status, result);
```

```python example=python
import os

import requests

response = requests.get(
    "https://api.rephut.com/v1/quotes/cm3u2qt0e0001quo6h7j8k9l",
    headers={
        "Authorization": f"Bearer {os.environ['FULFILLBOT_API_KEY']}",
    },
    timeout=30,
)
print(response.status_code, response.json())
```

```json example=response status=200
{
  "success": true,
  "data": {
    "id": "cm3u2qt0e0001quo6h7j8k9l",
    "number": "Q2611100001",
    "version": 1,
    "status": "AWAITING",
    "statusLabel": "Awaiting your decision",
    "canDecide": true,
    "issuedAt": "2026-11-10T07:00:00.000Z",
    "validUntil": "2026-11-17T07:00:00.000Z",
    "decidedAt": null,
    "destinationCountryCode": "US",
    "destination": "United States",
    "paymentMethodName": null,
    "note": "Price includes a branded sleeve.",
    "customerFeedback": "",
    "currency": "USD",
    "items": [
      {
        "sku": "FB-10412-NAT",
        "name": "Bamboo cutting board",
        "attributes": "38 x 25 cm",
        "customerSku": "",
        "qty": 300,
        "unitPriceCents": 240,
        "amountCents": 72000
      }
    ],
    "goodsCents": 72000,
    "shippingOptions": [
      {
        "label": "US Sea Freight",
        "transitDays": "30-40 days",
        "shippingCents": 21000,
        "totalCents": 93000,
        "shippingMaxCents": null,
        "totalMaxCents": null
      }
    ],
    "newerVersionId": null
  }
}
```
