# Sourcing and reorder requests

- A **sourcing request** asks us to find products. Give a link for each product (the API can't upload images), the quantity and, if you like, a target price. We reply with a quote: see [Quotes](https://docs.rephut.com/developers/reference/quotes).
- A **reorder request** asks us to restock products at your agreed prices. Only products with `canReorder: true` in [`GET /v1/products`](https://docs.rephut.com/developers/reference/products#list-your-products) can be reordered (`422 product_not_reorderable` otherwise). We turn it into a purchase order.

A request can have up to 20 products. Withdraw requests in the portal; the API can't withdraw them yet. The list is sorted newest first. 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).

## Request kinds

| Value | Meaning |
|---|---|
| `SOURCING` | Ask us to find and quote new products. |
| `REORDER` | Restock products at your agreed prices. |

## Request statuses

| Value | Meaning |
|---|---|
| `SUBMITTED` | We received the request. |
| `IN_PROGRESS` | We are working on it. |
| `QUOTED` | We sent a quote; `quoteId` points to it. |
| `DONE` | Completed, for example a purchase order was created (`purchaseOrderId`). |
| `CANCELLED` | Cancelled by you or by us; `cancelledByCustomer` and `cancelReason` say which and why. |

## Purposes

| Value | Meaning |
|---|---|
| `DROPSHIP_STOCK` | Stock kept in our warehouse for your dropship orders. |
| `SHIP_TO_ME` | Goods we ship to you. |
| `AMAZON_FBA` | Goods for Amazon FBA. |

## List sourcing and reorder requests

`GET /v1/requests`

Newest first.

| Property | Value |
|---|---|
| URL | `https://api.rephut.com/v1/requests` |
| Permission | `requests: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 | Request id. |
| `number` | string | Our request number. |
| `kind` | string | Sourcing or reorder. See [Request kinds](#request-kinds). One of `SOURCING`, `REORDER`. |
| `kindLabel` | string | English text for display. It may change; rely on the code next to it. |
| `status` | string | Request status. See [Request statuses](#request-statuses). One of `SUBMITTED`, `IN_PROGRESS`, `QUOTED`, `DONE`, `CANCELLED`. |
| `statusLabel` | string | English text for display. It may change; rely on the code next to it. |
| `itemCount` | integer | Number of products in the request. |
| `createdAt` | string (date-time) | When you submitted it. |
| `updatedAt` | string (date-time) | When it last changed. |

### 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/requests?page=1' \
  -H "Authorization: Bearer $FULFILLBOT_API_KEY"
```

```js example=node
const response = await fetch("https://api.rephut.com/v1/requests?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/requests?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": "cm3r8eq0s0002src2a3s4d5g",
      "number": "R2611090002",
      "kind": "REORDER",
      "kindLabel": "Reorder",
      "status": "SUBMITTED",
      "statusLabel": "Submitted",
      "itemCount": 1,
      "createdAt": "2026-11-09T06:00:00.000Z",
      "updatedAt": "2026-11-09T06:00:00.000Z"
    },
    {
      "id": "cm3r8eq0s0001src2a3s4d5f",
      "number": "R2611090001",
      "kind": "SOURCING",
      "kindLabel": "Sourcing request",
      "status": "SUBMITTED",
      "statusLabel": "Submitted",
      "itemCount": 1,
      "createdAt": "2026-11-09T06:00:00.000Z",
      "updatedAt": "2026-11-09T06:00:00.000Z"
    }
  ],
  "meta": {
    "total": 2,
    "page": 1,
    "limit": 50,
    "pageCount": 1
  }
}
```

## Get a sourcing or reorder request

`GET /v1/requests/{id}`

| Property | Value |
|---|---|
| URL | `https://api.rephut.com/v1/requests/{id}` |
| Permission | `requests: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 | Request id, from [`GET /v1/requests`](https://docs.rephut.com/developers/reference/requests#list-sourcing-and-reorder-requests). 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 | Request id. |
| `number` | string | Our request number. |
| `kind` | string | Sourcing or reorder. See [Request kinds](#request-kinds). One of `SOURCING`, `REORDER`. |
| `kindLabel` | string | English text for display. It may change; rely on the code next to it. |
| `status` | string | Request status. See [Request statuses](#request-statuses). One of `SUBMITTED`, `IN_PROGRESS`, `QUOTED`, `DONE`, `CANCELLED`. |
| `statusLabel` | string | English text for display. It may change; rely on the code next to it. |
| `itemCount` | integer | Number of products in the request. |
| `createdAt` | string (date-time) | When you submitted it. |
| `updatedAt` | string (date-time) | When it last changed. |
| `purpose` | string | What the goods are for. See [Purposes](#purposes). One of `DROPSHIP_STOCK`, `SHIP_TO_ME`, `AMAZON_FBA`. |
| `purposeLabel` | string | English text for display. It may change; rely on the code next to it. |
| `countryCode` | string or null | Destination country of a sourcing request; null for reorders. |
| `neededBy` | string (date) or null | When you need the goods (YYYY-MM-DD, China time); null when you gave no date. |
| `note` | string | Your note. |
| `staffReply` | string | Our reply; empty until we reply. |
| `cancelReason` | string | Why the request was cancelled; empty otherwise. |
| `cancelledByCustomer` | boolean | `true` when you withdrew it. |
| `canCancel` | boolean | `true` while you can still withdraw it. Withdraw requests in the portal; the API can't withdraw them. |
| `quoteId` | string or null | Our quote for this request; null until we send one. Fetch it with [`GET /v1/quotes/{id}`](https://docs.rephut.com/developers/reference/quotes#get-a-quote). |
| `quoteNumber` | string or null | Quote number; null until we send one. |
| `purchaseOrderId` | string or null | The purchase order made from this request; null until there is one. Fetch it with [`GET /v1/purchase-orders/{id}`](https://docs.rephut.com/developers/reference/purchase-orders#get-a-purchase-order). |
| `purchaseOrderNumber` | string or null | Purchase order number; null until there is one. |
| `currency` | string | Currency of every amount. Always `USD`. |
| `totalCents` | integer or null | Reorders only: agreed price times quantity. |
| `items` | array of objects | Products in the request. |
| `items[].title` | string | Product name. |
| `items[].productUrl` | string | Product link you gave (sourcing). |
| `items[].specs` | string | Details you gave: size, colour, material. |
| `items[].qty` | integer | Quantity. |
| `items[].targetPriceCents` | integer or null | Your target unit price in US cents; null when none. |
| `items[].sku` | string | Our SKU (reorders); empty otherwise. |
| `items[].agreedPriceCents` | integer or null | Your agreed unit price in US cents (reorders); null otherwise. |

### 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/requests/cm3r8eq0s0001src2a3s4d5f' \
  -H "Authorization: Bearer $FULFILLBOT_API_KEY"
```

```js example=node
const response = await fetch("https://api.rephut.com/v1/requests/cm3r8eq0s0001src2a3s4d5f", {
  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/requests/cm3r8eq0s0001src2a3s4d5f",
    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": "cm3r8eq0s0001src2a3s4d5f",
    "number": "R2611090001",
    "kind": "SOURCING",
    "kindLabel": "Sourcing request",
    "status": "SUBMITTED",
    "statusLabel": "Submitted",
    "itemCount": 1,
    "createdAt": "2026-11-09T06:00:00.000Z",
    "updatedAt": "2026-11-09T06:00:00.000Z",
    "purpose": "DROPSHIP_STOCK",
    "purposeLabel": "Dropshipping stock",
    "countryCode": "US",
    "neededBy": "2026-12-01",
    "note": "Looking for a supplier with fast handling.",
    "staffReply": "",
    "cancelReason": "",
    "cancelledByCustomer": false,
    "canCancel": true,
    "quoteId": null,
    "quoteNumber": null,
    "purchaseOrderId": null,
    "purchaseOrderNumber": null,
    "currency": "USD",
    "totalCents": null,
    "items": [
      {
        "title": "Bamboo cutting board",
        "productUrl": "https://www.example.com/products/bamboo-board",
        "specs": "38 x 25 cm, with juice groove",
        "qty": 300,
        "targetPriceCents": 250,
        "sku": "",
        "agreedPriceCents": null
      }
    ]
  }
}
```

## Ask us to source new products

`POST /v1/requests/sourcing`

Submits a sourcing request like the portal's New request form. Give a product link for each product (the API can't upload images). We reply with a quote: follow it with GET /v1/requests/{id}. Send an Idempotency-Key so a retry can't submit it twice.

| Property | Value |
|---|---|
| URL | `https://api.rephut.com/v1/requests/sourcing` |
| Permission | `requests:write` |
| Access | Write: counts towards the write limits and is refused with `account_on_hold` while your account is on hold |
| Idempotency-Key | Required |
| Success | 201 Created |

### Headers

| Header | Required | Value |
|---|---|---|
| `Authorization` | Yes | `Bearer <your API key>`. See [Authentication](https://docs.rephut.com/developers/authentication). |
| `Content-Type` | Yes | `application/json` |
| `Idempotency-Key` | Yes | 1 to 255 visible ASCII characters, new for each new request. See [Idempotency](https://docs.rephut.com/developers/idempotency). |

### Request body

A JSON object. Fields not listed here are ignored.

| Field | Type | Required | Description |
|---|---|---|---|
| `countryCode` | string | Yes | Destination country, two-letter code such as US. Pattern: `^[A-Z]{2}$`. |
| `purpose` | string | Yes | What the goods are for: DROPSHIP_STOCK, SHIP_TO_ME, AMAZON_FBA. One of `DROPSHIP_STOCK`, `SHIP_TO_ME`, `AMAZON_FBA`. |
| `neededBy` | string or null | No | When you need the goods, YYYY-MM-DD; null if there is no date. Default: `null`. |
| `note` | string | No | Anything else we should know. Up to 1,000 characters. Default: `""`. |
| `items` | array of objects | Yes | Products you want us to source. 1 to 20 items. |
| `items[].productUrl` | string | Yes | Link to the product you want, starting with http:// or https://. Up to 1,000 characters. |
| `items[].title` | string | Yes | Product name. 1 to 200 characters. |
| `items[].specs` | string | No | Size, colour, material and other details. Up to 1,000 characters. Default: `""`. |
| `items[].sourceCustomerSku` | string | No | Your SKU for the product, if you have one. Up to 100 characters. Default: `""`. |
| `items[].qty` | integer | Yes | Quantity you want. From 1 to 1,000,000. |
| `items[].targetPriceCents` | integer or null | No | Target unit price in US cents; null if you have none. From 0 to 999,999,999. Default: `null`. |

### Response

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

| Field | Type | Description |
|---|---|---|
| `id` | string | Request id. |
| `number` | string | Our request number. |
| `kind` | string | Sourcing or reorder. See [Request kinds](#request-kinds). One of `SOURCING`, `REORDER`. |
| `kindLabel` | string | English text for display. It may change; rely on the code next to it. |
| `status` | string | Request status. See [Request statuses](#request-statuses). One of `SUBMITTED`, `IN_PROGRESS`, `QUOTED`, `DONE`, `CANCELLED`. |
| `statusLabel` | string | English text for display. It may change; rely on the code next to it. |
| `itemCount` | integer | Number of products in the request. |
| `createdAt` | string (date-time) | When you submitted it. |
| `updatedAt` | string (date-time) | When it last changed. |
| `purpose` | string | What the goods are for. See [Purposes](#purposes). One of `DROPSHIP_STOCK`, `SHIP_TO_ME`, `AMAZON_FBA`. |
| `purposeLabel` | string | English text for display. It may change; rely on the code next to it. |
| `countryCode` | string or null | Destination country of a sourcing request; null for reorders. |
| `neededBy` | string (date) or null | When you need the goods (YYYY-MM-DD, China time); null when you gave no date. |
| `note` | string | Your note. |
| `staffReply` | string | Our reply; empty until we reply. |
| `cancelReason` | string | Why the request was cancelled; empty otherwise. |
| `cancelledByCustomer` | boolean | `true` when you withdrew it. |
| `canCancel` | boolean | `true` while you can still withdraw it. Withdraw requests in the portal; the API can't withdraw them. |
| `quoteId` | string or null | Our quote for this request; null until we send one. Fetch it with [`GET /v1/quotes/{id}`](https://docs.rephut.com/developers/reference/quotes#get-a-quote). |
| `quoteNumber` | string or null | Quote number; null until we send one. |
| `purchaseOrderId` | string or null | The purchase order made from this request; null until there is one. Fetch it with [`GET /v1/purchase-orders/{id}`](https://docs.rephut.com/developers/reference/purchase-orders#get-a-purchase-order). |
| `purchaseOrderNumber` | string or null | Purchase order number; null until there is one. |
| `currency` | string | Currency of every amount. Always `USD`. |
| `totalCents` | integer or null | Reorders only: agreed price times quantity. |
| `items` | array of objects | Products in the request. |
| `items[].title` | string | Product name. |
| `items[].productUrl` | string | Product link you gave (sourcing). |
| `items[].specs` | string | Details you gave: size, colour, material. |
| `items[].qty` | integer | Quantity. |
| `items[].targetPriceCents` | integer or null | Your target unit price in US cents; null when none. |
| `items[].sku` | string | Our SKU (reorders); empty otherwise. |
| `items[].agreedPriceCents` | integer or null | Your agreed unit price in US cents (reorders); null otherwise. |

### Errors

| Code | HTTP status | Meaning |
|---|---|---|
| `idempotency_key_required` | 400 | This request needs an Idempotency-Key header. |
| `invalid_idempotency_key` | 400 | Idempotency-Key must be 1 to 255 visible ASCII characters. |
| `invalid_json` | 400 | The request body is not valid JSON. |
| `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. |
| `account_on_hold` | 403 | The account is on hold. Writes are refused; reads still work. |
| `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. |
| `request_in_progress` | 409 | A request with this Idempotency-Key is still running or its result is unknown. |
| `payload_too_large` | 413 | The request body is larger than 1 MB. |
| `unsupported_media_type` | 415 | The request body must be sent as application/json. |
| `idempotency_key_reused` | 422 | This Idempotency-Key was already used with a different request. |
| `request_rejected` | 422 | The request was rejected by a business rule. |
| `unknown_country` | 422 | Choose your country from the list. |
| `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 -X POST 'https://api.rephut.com/v1/requests/sourcing' \
  -H "Authorization: Bearer $FULFILLBOT_API_KEY" \
  -H "Content-Type: application/json" \
  -H 'Idempotency-Key: 7f3e9b21-0c4d-4a8e-b5f6-1e2d3c4b5a69' \
  -d '{
  "countryCode": "US",
  "purpose": "DROPSHIP_STOCK",
  "neededBy": "2026-12-01",
  "note": "Looking for a supplier with fast handling.",
  "items": [
    {
      "productUrl": "https://www.example.com/products/bamboo-board",
      "title": "Bamboo cutting board",
      "specs": "38 x 25 cm, with juice groove",
      "sourceCustomerSku": "",
      "qty": 300,
      "targetPriceCents": 250
    }
  ]
}'
```

```js example=node
const response = await fetch("https://api.rephut.com/v1/requests/sourcing", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.FULFILLBOT_API_KEY}`,
    "Content-Type": "application/json",
    "Idempotency-Key": "7f3e9b21-0c4d-4a8e-b5f6-1e2d3c4b5a69",
  },
  body: JSON.stringify({
    "countryCode": "US",
    "purpose": "DROPSHIP_STOCK",
    "neededBy": "2026-12-01",
    "note": "Looking for a supplier with fast handling.",
    "items": [
      {
        "productUrl": "https://www.example.com/products/bamboo-board",
        "title": "Bamboo cutting board",
        "specs": "38 x 25 cm, with juice groove",
        "sourceCustomerSku": "",
        "qty": 300,
        "targetPriceCents": 250
      }
    ]
  }),
});
const result = await response.json();
console.log(response.status, result);
```

```python example=python
import os

import requests

response = requests.post(
    "https://api.rephut.com/v1/requests/sourcing",
    headers={
        "Authorization": f"Bearer {os.environ['FULFILLBOT_API_KEY']}",
        "Idempotency-Key": "7f3e9b21-0c4d-4a8e-b5f6-1e2d3c4b5a69",
    },
    json={
        "countryCode": "US",
        "purpose": "DROPSHIP_STOCK",
        "neededBy": "2026-12-01",
        "note": "Looking for a supplier with fast handling.",
        "items": [
            {
                "productUrl": "https://www.example.com/products/bamboo-board",
                "title": "Bamboo cutting board",
                "specs": "38 x 25 cm, with juice groove",
                "sourceCustomerSku": "",
                "qty": 300,
                "targetPriceCents": 250,
            },
        ],
    },
    timeout=30,
)
print(response.status_code, response.json())
```

```json example=response status=201
{
  "success": true,
  "data": {
    "id": "cm3r8eq0s0001src2a3s4d5f",
    "number": "R2611090001",
    "kind": "SOURCING",
    "kindLabel": "Sourcing request",
    "status": "SUBMITTED",
    "statusLabel": "Submitted",
    "itemCount": 1,
    "createdAt": "2026-11-09T06:00:00.000Z",
    "updatedAt": "2026-11-09T06:00:00.000Z",
    "purpose": "DROPSHIP_STOCK",
    "purposeLabel": "Dropshipping stock",
    "countryCode": "US",
    "neededBy": "2026-12-01",
    "note": "Looking for a supplier with fast handling.",
    "staffReply": "",
    "cancelReason": "",
    "cancelledByCustomer": false,
    "canCancel": true,
    "quoteId": null,
    "quoteNumber": null,
    "purchaseOrderId": null,
    "purchaseOrderNumber": null,
    "currency": "USD",
    "totalCents": null,
    "items": [
      {
        "title": "Bamboo cutting board",
        "productUrl": "https://www.example.com/products/bamboo-board",
        "specs": "38 x 25 cm, with juice groove",
        "qty": 300,
        "targetPriceCents": 250,
        "sku": "",
        "agreedPriceCents": null
      }
    ]
  }
}
```

## Ask us to restock products at your agreed prices

`POST /v1/requests/reorder`

Submits a reorder request like the portal's Reorder form. Only products with an agreed price can be reordered (canReorder in GET /v1/products); others return 422 product_not_reorderable. Send an Idempotency-Key so a retry can't submit it twice.

| Property | Value |
|---|---|
| URL | `https://api.rephut.com/v1/requests/reorder` |
| Permission | `requests:write` |
| Access | Write: counts towards the write limits and is refused with `account_on_hold` while your account is on hold |
| Idempotency-Key | Required |
| Success | 201 Created |

### Headers

| Header | Required | Value |
|---|---|---|
| `Authorization` | Yes | `Bearer <your API key>`. See [Authentication](https://docs.rephut.com/developers/authentication). |
| `Content-Type` | Yes | `application/json` |
| `Idempotency-Key` | Yes | 1 to 255 visible ASCII characters, new for each new request. See [Idempotency](https://docs.rephut.com/developers/idempotency). |

### Request body

A JSON object. Fields not listed here are ignored.

| Field | Type | Required | Description |
|---|---|---|---|
| `purpose` | string | Yes | What the goods are for: DROPSHIP_STOCK, SHIP_TO_ME, AMAZON_FBA. One of `DROPSHIP_STOCK`, `SHIP_TO_ME`, `AMAZON_FBA`. |
| `note` | string | No | Anything else we should know. Up to 1,000 characters. Default: `""`. |
| `items` | array of objects | Yes | Products you can reorder (canReorder in GET /v1/products), each once, with quantities. 1 to 20 items. |
| `items[].productId` | string | Yes | Our product id; the product must have `canReorder: true`. Not empty. |
| `items[].qty` | integer | Yes | Quantity to buy. From 1 to 1,000,000. |

### Response

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

| Field | Type | Description |
|---|---|---|
| `id` | string | Request id. |
| `number` | string | Our request number. |
| `kind` | string | Sourcing or reorder. See [Request kinds](#request-kinds). One of `SOURCING`, `REORDER`. |
| `kindLabel` | string | English text for display. It may change; rely on the code next to it. |
| `status` | string | Request status. See [Request statuses](#request-statuses). One of `SUBMITTED`, `IN_PROGRESS`, `QUOTED`, `DONE`, `CANCELLED`. |
| `statusLabel` | string | English text for display. It may change; rely on the code next to it. |
| `itemCount` | integer | Number of products in the request. |
| `createdAt` | string (date-time) | When you submitted it. |
| `updatedAt` | string (date-time) | When it last changed. |
| `purpose` | string | What the goods are for. See [Purposes](#purposes). One of `DROPSHIP_STOCK`, `SHIP_TO_ME`, `AMAZON_FBA`. |
| `purposeLabel` | string | English text for display. It may change; rely on the code next to it. |
| `countryCode` | string or null | Destination country of a sourcing request; null for reorders. |
| `neededBy` | string (date) or null | When you need the goods (YYYY-MM-DD, China time); null when you gave no date. |
| `note` | string | Your note. |
| `staffReply` | string | Our reply; empty until we reply. |
| `cancelReason` | string | Why the request was cancelled; empty otherwise. |
| `cancelledByCustomer` | boolean | `true` when you withdrew it. |
| `canCancel` | boolean | `true` while you can still withdraw it. Withdraw requests in the portal; the API can't withdraw them. |
| `quoteId` | string or null | Our quote for this request; null until we send one. Fetch it with [`GET /v1/quotes/{id}`](https://docs.rephut.com/developers/reference/quotes#get-a-quote). |
| `quoteNumber` | string or null | Quote number; null until we send one. |
| `purchaseOrderId` | string or null | The purchase order made from this request; null until there is one. Fetch it with [`GET /v1/purchase-orders/{id}`](https://docs.rephut.com/developers/reference/purchase-orders#get-a-purchase-order). |
| `purchaseOrderNumber` | string or null | Purchase order number; null until there is one. |
| `currency` | string | Currency of every amount. Always `USD`. |
| `totalCents` | integer or null | Reorders only: agreed price times quantity. |
| `items` | array of objects | Products in the request. |
| `items[].title` | string | Product name. |
| `items[].productUrl` | string | Product link you gave (sourcing). |
| `items[].specs` | string | Details you gave: size, colour, material. |
| `items[].qty` | integer | Quantity. |
| `items[].targetPriceCents` | integer or null | Your target unit price in US cents; null when none. |
| `items[].sku` | string | Our SKU (reorders); empty otherwise. |
| `items[].agreedPriceCents` | integer or null | Your agreed unit price in US cents (reorders); null otherwise. |

### Errors

| Code | HTTP status | Meaning |
|---|---|---|
| `idempotency_key_required` | 400 | This request needs an Idempotency-Key header. |
| `invalid_idempotency_key` | 400 | Idempotency-Key must be 1 to 255 visible ASCII characters. |
| `invalid_json` | 400 | The request body is not valid JSON. |
| `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. |
| `account_on_hold` | 403 | The account is on hold. Writes are refused; reads still work. |
| `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. |
| `request_in_progress` | 409 | A request with this Idempotency-Key is still running or its result is unknown. |
| `payload_too_large` | 413 | The request body is larger than 1 MB. |
| `unsupported_media_type` | 415 | The request body must be sent as application/json. |
| `idempotency_key_reused` | 422 | This Idempotency-Key was already used with a different request. |
| `product_not_reorderable` | 422 | One of the products is no longer available to reorder. Please refresh the page and try again. |
| `request_rejected` | 422 | The request was rejected by a business rule. |
| `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 -X POST 'https://api.rephut.com/v1/requests/reorder' \
  -H "Authorization: Bearer $FULFILLBOT_API_KEY" \
  -H "Content-Type: application/json" \
  -H 'Idempotency-Key: e8d7c6b5-a4f3-4e21-9d0c-8b7a6f5e4d32' \
  -d '{
  "purpose": "DROPSHIP_STOCK",
  "note": "",
  "items": [
    {
      "productId": "cm3p4r0d0001xyz7k2m9q8w1",
      "qty": 200
    }
  ]
}'
```

```js example=node
const response = await fetch("https://api.rephut.com/v1/requests/reorder", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.FULFILLBOT_API_KEY}`,
    "Content-Type": "application/json",
    "Idempotency-Key": "e8d7c6b5-a4f3-4e21-9d0c-8b7a6f5e4d32",
  },
  body: JSON.stringify({
    "purpose": "DROPSHIP_STOCK",
    "note": "",
    "items": [
      {
        "productId": "cm3p4r0d0001xyz7k2m9q8w1",
        "qty": 200
      }
    ]
  }),
});
const result = await response.json();
console.log(response.status, result);
```

```python example=python
import os

import requests

response = requests.post(
    "https://api.rephut.com/v1/requests/reorder",
    headers={
        "Authorization": f"Bearer {os.environ['FULFILLBOT_API_KEY']}",
        "Idempotency-Key": "e8d7c6b5-a4f3-4e21-9d0c-8b7a6f5e4d32",
    },
    json={
        "purpose": "DROPSHIP_STOCK",
        "note": "",
        "items": [
            {
                "productId": "cm3p4r0d0001xyz7k2m9q8w1",
                "qty": 200,
            },
        ],
    },
    timeout=30,
)
print(response.status_code, response.json())
```

```json example=response status=201
{
  "success": true,
  "data": {
    "id": "cm3r8eq0s0002src2a3s4d5g",
    "number": "R2611090002",
    "kind": "REORDER",
    "kindLabel": "Reorder",
    "status": "SUBMITTED",
    "statusLabel": "Submitted",
    "itemCount": 1,
    "createdAt": "2026-11-09T06:00:00.000Z",
    "updatedAt": "2026-11-09T06:00:00.000Z",
    "purpose": "DROPSHIP_STOCK",
    "purposeLabel": "Dropshipping stock",
    "countryCode": null,
    "neededBy": null,
    "note": "",
    "staffReply": "",
    "cancelReason": "",
    "cancelledByCustomer": false,
    "canCancel": true,
    "quoteId": null,
    "quoteNumber": null,
    "purchaseOrderId": null,
    "purchaseOrderNumber": null,
    "currency": "USD",
    "totalCents": 77000,
    "items": [
      {
        "title": "Cotton crew neck T-shirt",
        "productUrl": "",
        "specs": "Black / M",
        "qty": 200,
        "targetPriceCents": null,
        "sku": "FB-10231-BLK-M",
        "agreedPriceCents": 385
      }
    ]
  }
}
```
