# Purchase orders

A purchase order is what we buy for you from a seller: from a reorder or sourcing request, from auto purchase when orders are short of stock, or one you placed yourself from Search in the portal. When its goods arrive and are checked, they become stock in your inventory.

You can read purchase orders here. Purchase orders from auto purchase are paid from your wallet automatically when your available balance covers them; otherwise they wait and are paid automatically after a top-up ([Charges and your wallet](https://docs.rephut.com/developers/charges)). Pay all other purchase orders in the portal; the API can't pay yet. Webhook `purchase_order.status_changed` reports every status change. The list is sorted newest first and includes cancelled purchase orders. 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).

## Purchase order statuses

| Value | Meaning |
|---|---|
| `AWAITING_PAYMENT` | Waiting for your payment. Pay purchase orders in the portal. |
| `DEPOSIT_PAID` | We received a deposit; the rest is due later. |
| `PAID` | Paid in full; we are about to buy the goods. |
| `PURCHASING` | We are buying the goods from the seller. The statuses from here to `ARRIVED` say how far the goods have got, not whether you have paid; payment may still be partly or fully due. |
| `ORDERED` | Ordered from the seller (shown as Purchasing in the portal). |
| `IN_TRANSIT` | On the way to our warehouse. |
| `PARTIALLY_ARRIVED` | Some of the goods arrived at our warehouse. |
| `ARRIVED` | All goods arrived at our warehouse. |
| `STORED` | All goods are in stock in our warehouse (or settled as short); some payment is still due. |
| `COMPLETED` | Paid in full and all goods are in stock (or settled as short). |
| `CANCELLED` | Cancelled. `cancelledAt` says when. |

## List purchase orders

`GET /v1/purchase-orders`

All your purchase orders, including cancelled ones. Newest first.

| Property | Value |
|---|---|
| URL | `https://api.rephut.com/v1/purchase-orders` |
| Permission | `purchase_orders: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 | Purchase order id. |
| `number` | string | Our purchase order number. |
| `status` | string | Purchase order status. See [Purchase order statuses](#purchase-order-statuses). One of `AWAITING_PAYMENT`, `DEPOSIT_PAID`, `PAID`, `PURCHASING`, `ORDERED`, `IN_TRANSIT`, `PARTIALLY_ARRIVED`, `ARRIVED`, `STORED`, `COMPLETED`, `CANCELLED`. |
| `statusLabel` | string | English text for display. It may change; rely on the code next to it. |
| `itemCount` | integer | Number of products on the purchase order. |
| `currency` | string | Currency of every amount. Always `USD`. |
| `receivableCents` | integer | What you pay: the total minus short-shipped items. |
| `paidCents` | integer | Paid so far minus refunds, in US cents. |
| `dueCents` | integer | Still to pay, in US cents; 0 when the purchase order is cancelled. |
| `createdAt` | string (date-time) | When the purchase order was created. |

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

```js example=node
const response = await fetch("https://api.rephut.com/v1/purchase-orders?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/purchase-orders?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": "cm3q1po0r0001der5t6y7u8i",
      "number": "PO2611040003",
      "status": "IN_TRANSIT",
      "statusLabel": "On the way to warehouse",
      "itemCount": 1,
      "currency": "USD",
      "receivableCents": 78500,
      "paidCents": 78500,
      "dueCents": 0,
      "createdAt": "2026-11-04T05:00:00.000Z"
    }
  ],
  "meta": {
    "total": 1,
    "page": 1,
    "limit": 50,
    "pageCount": 1
  }
}
```

## Get a purchase order

`GET /v1/purchase-orders/{id}`

| Property | Value |
|---|---|
| URL | `https://api.rephut.com/v1/purchase-orders/{id}` |
| Permission | `purchase_orders: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 | Purchase order id, from [`GET /v1/purchase-orders`](https://docs.rephut.com/developers/reference/purchase-orders#list-purchase-orders). 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 | Purchase order id. |
| `number` | string | Our purchase order number. |
| `status` | string | Purchase order status. See [Purchase order statuses](#purchase-order-statuses). One of `AWAITING_PAYMENT`, `DEPOSIT_PAID`, `PAID`, `PURCHASING`, `ORDERED`, `IN_TRANSIT`, `PARTIALLY_ARRIVED`, `ARRIVED`, `STORED`, `COMPLETED`, `CANCELLED`. |
| `statusLabel` | string | English text for display. It may change; rely on the code next to it. |
| `createdAt` | string (date-time) | When the purchase order was created. |
| `cancelledAt` | string (date-time) or null | When it was cancelled; null otherwise. |
| `paymentMethodName` | string or null | The payment method set for this purchase order; null when none. |
| `note` | string | Note on the purchase order. |
| `placedViaSearch` | boolean | `true` when you placed it yourself from Search in the portal. |
| `currency` | string | Currency of every amount. Always `USD`. |
| `items` | array of objects | Products on the purchase order. |
| `items[].productId` | string | Our product id. |
| `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 when it is mapped; empty otherwise. |
| `items[].qty` | integer | Quantity on the purchase order. |
| `items[].unitPriceCents` | integer | Unit price in US cents. |
| `items[].amountCents` | integer | Line amount in US cents. |
| `items[].orderedQty` | integer | Quantity we have ordered from the seller. |
| `items[].inWarehouseQty` | integer | Quantity received in our warehouse in good condition. |
| `items[].shortQty` | integer | Short-shipped or refunded by the seller. |
| `charges` | array of objects | Other charges on the purchase order, such as domestic shipping. |
| `charges[].label` | string | What the charge is for. |
| `charges[].amountCents` | integer | Amount in US cents. |
| `totalCents` | integer | Goods plus charges, in US cents. |
| `shortageCents` | integer | Value of short-shipped or refunded items, in US cents. |
| `receivableCents` | integer | What you pay: `totalCents` minus `shortageCents`. |
| `paidCents` | integer | Paid so far minus refunds, in US cents. |
| `dueCents` | integer | Still to pay, in US cents; 0 when the purchase order is cancelled. |
| `payments` | array of objects | Payments received. |
| `payments[].paidOn` | string (date) | The day of the payment (YYYY-MM-DD, China time). |
| `payments[].amountCents` | integer | Amount in US cents. |
| `payments[].methodLabel` | string | Payment method. |
| `refunds` | array of objects | Refunds to you. |
| `refunds[].label` | string | What was refunded. |
| `refunds[].amountCents` | integer | Amount in US cents. |
| `refunds[].refundedAt` | string (date-time) | When it was refunded. |
| `paymentInstructions` | array of objects | How to pay; empty when nothing is due. |
| `paymentInstructions[].name` | string | Payment method name. |
| `paymentInstructions[].instructions` | string | How to pay with this method. |

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

```js example=node
const response = await fetch("https://api.rephut.com/v1/purchase-orders/cm3q1po0r0001der5t6y7u8i", {
  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/purchase-orders/cm3q1po0r0001der5t6y7u8i",
    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": "cm3q1po0r0001der5t6y7u8i",
    "number": "PO2611040003",
    "status": "IN_TRANSIT",
    "statusLabel": "On the way to warehouse",
    "createdAt": "2026-11-04T05:00:00.000Z",
    "cancelledAt": null,
    "paymentMethodName": "Wallet",
    "note": "",
    "placedViaSearch": false,
    "currency": "USD",
    "items": [
      {
        "productId": "cm3p4r0d0001xyz7k2m9q8w1",
        "sku": "FB-10231-BLK-M",
        "name": "Cotton crew neck T-shirt",
        "attributes": "Black / M",
        "customerSku": "TEE-BLK-M",
        "qty": 200,
        "unitPriceCents": 385,
        "amountCents": 77000,
        "orderedQty": 200,
        "inWarehouseQty": 0,
        "shortQty": 0
      }
    ],
    "charges": [
      {
        "label": "Domestic shipping",
        "amountCents": 1500
      }
    ],
    "totalCents": 78500,
    "shortageCents": 0,
    "receivableCents": 78500,
    "paidCents": 78500,
    "dueCents": 0,
    "payments": [
      {
        "paidOn": "2026-11-04",
        "amountCents": 78500,
        "methodLabel": "Wallet balance"
      }
    ],
    "refunds": [],
    "paymentInstructions": []
  }
}
```
