# Work with purchase orders

> What the public API can do with purchase orders today (vendors, links to sales orders, exports, history, reports, and the two operations YoPrint's Zapier app uses) and where to create and receive them instead.

Web page: https://developer.yoprint.com/guides/purchase-orders/

**Read first:** [Purchasing and stock](https://developer.yoprint.com/how-yoprint-works/purchasing-and-stock.md), for purchase orders, needs ordering, receiving and stock.

A purchase order (PO) buys blanks and supplies from a vendor. Receiving a PO records what arrived, and moves it into stock.

> **Caution: No create or receive in the public API**
>
> The public API can't create or receive purchase orders, or change their items. Create and receive them in the YoPrint web app.

What the public API can do with purchasing today:

| To                                                   | Operations                                   |
| ---------------------------------------------------- | -------------------------------------------- |
| Manage vendors and their contacts                    | Vendors                                      |
| Link purchase orders to the sales orders they supply | Sales order PO links                         |
| Export purchase orders to CSV                        | PO export, data export                       |
| Read a PO's history                                  | PO history                                   |
| Report on purchasing                                 | PO report                                    |
| React when a PO's status changes                     | The `purchase_order.status_changed` webhook  |
| Read one PO, or change its status                    | Two operations kept for YoPrint's Zapier app |

## 1. Manage vendors

`GET /v1/api/store/{team_slug}/vendor` [List vendors](https://developer.yoprint.com/reference/vendors/v1-vendor-index.md)

`POST /v1/api/store/{team_slug}/vendor` [Create a vendor](https://developer.yoprint.com/reference/vendors/v1-vendor-store.md)

`PUT /v1/api/store/{team_slug}/vendor/{hash_id}` [Update a vendor](https://developer.yoprint.com/reference/vendors/v1-vendor-update-put.md)

Vendors have contacts, like customers do:

`POST /v1/api/store/{team_slug}/vendor/{hash_id}/contact` [Add a contact to a vendor](https://developer.yoprint.com/reference/vendor-contacts/v1-vendor-contact-store.md)

## 2. Link a purchase order to a sales order

Linking shows which POs supply which sales order. Add the PO ids in `item`.

The public API has no PO list, so take a PO's `id` from a `purchase_order.status_changed` webhook, or from the links already on another sales order.

`PUT /v2/api/store/{team_slug}/v2/sales_order/{hash_id}/purchase_order_link/add` [Link purchase orders to a sales order](https://developer.yoprint.com/reference/sales-order-purchase-orders/v2-purchase-order-link-add.md)

```bash
curl -X PUT https://secure.yoprint.com/v2/api/store/your-team/v2/sales_order/V4keXwwxMQyXw7/purchase_order_link/add \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Accept: application/json" \
  -H "X-Yoprint-Decimal-Format: exact-v1" \
  -H "Content-Type: application/json" \
  -d '{ "item": [{ "id": "oovEHf2HhdHhwR" }] }'
```

Only sales orders take links, not quotes. To see and remove links:

`GET /v2/api/store/{team_slug}/v2/sales_order/{hash_id}/purchase_order_link` [List the purchase orders linked to an order](https://developer.yoprint.com/reference/sales-order-purchase-orders/v2-purchase-order-link-index.md)

`PUT /v2/api/store/{team_slug}/v2/sales_order/{hash_id}/purchase_order_link/remove` [Unlink purchase orders from an order](https://developer.yoprint.com/reference/sales-order-purchase-orders/v2-purchase-order-link-remove.md)

## 3. Export purchase orders

For a CSV of the POs issued in a date range:

`POST /v1/api/store/{team_slug}/purchase_order/export` [Export purchase orders by issue date](https://developer.yoprint.com/reference/purchase-orders/v1-purchase-order-export.md)

```json
{ "start_date": "2026-10-01T00:00:00Z", "end_date": "2026-10-31T23:59:59Z" }
```

Add `"list_only": true` to get the same rows as JSON instead of a CSV file. The rows carry PO numbers, not ids. For the CSV, when no PO matches the answer is `422` with "Purchase Order not found". With `list_only: true` it returns an empty `data` list. In both formats, amounts such as `ordered_total` and `received_total` are decimal strings (such as `"120.00"`). See [Money and quantities](https://developer.yoprint.com/concepts/money-and-quantities.md).

For a whole-team list, use the queued data export. See [Export and import data](https://developer.yoprint.com/guides/imports-and-exports.md).

`POST /v1/api/store/{team_slug}/data_export/export_purchase_order_list` [Export purchase orders in a date range to CSV](https://developer.yoprint.com/reference/data-exports/v1-data-export-export-purchase-order-list.md)

## 4. Read history and report

`GET /v1/api/store/{team_slug}/purchase_order/{hash_id}/history` [List a purchase order's history](https://developer.yoprint.com/reference/purchase-orders/v1-purchase-order-history.md)

`POST /v1/api/store/{team_slug}/report/purchase_order` [Report purchase orders by issue date](https://developer.yoprint.com/reference/reports/v1-report-purchase-order.md)

In the purchase order report, amounts (`calc_subtotal`, `calc_tax`, `calc_total`) are decimal strings with 4 decimal places, such as `"125.0000"`. Parse them as decimals. See [Money and quantities](https://developer.yoprint.com/concepts/money-and-quantities.md).

PO statuses, for filters and webhooks:

`GET /v1/api/store/{team_slug}/setting/status/purchase_order` [List purchase order statuses](https://developer.yoprint.com/reference/statuses/v1-status-list-purchase-order.md)

## 5. Follow status changes

Subscribe to `purchase_order.status_changed` to hear when a PO's status is set, for example to Completed when everything has arrived. It fires on every status change call, even when the status stays the same. Narrow it to some statuses with `purchase_order_status_changed_options`. See [Receive events with webhooks](https://developer.yoprint.com/guides/webhooks.md).

## 6. The Zapier app's purchase order operations

YoPrint's Zapier app finds a purchase order by its id and changes its status with these two operations. They exist so that Zaps keep working, and their answer keeps the shape the Zapier app reads:

`GET /v1/api/store/{team_slug}/purchase_order/{hash_id}` [Get a purchase order](https://developer.yoprint.com/reference/purchase-orders/v1-purchase-order-show.md)

`PUT /v1/api/store/{team_slug}/purchase_order/{hash_id}/status` [Change a purchase order's status](https://developer.yoprint.com/reference/purchase-orders/v1-purchase-order-change-status.md)

- Reading needs permission to view purchase orders, and changing the status needs permission to update them. A role that sees only its own POs reaches only the POs its user created or is assigned to.
- Money in `v1_public_purchase_order_show` is in **thousandths** (`10800` is 10.80): `calc_subtotal`, `calc_tax` and `calc_total` on the purchase order and on each line, and each line's `price`. Usually a whole number of thousandths. An amount finer than a thousandth comes back as a decimal string such as `"12656.3"`. Parse them as decimals. See [Money and quantities](https://developer.yoprint.com/concepts/money-and-quantities.md).
- In lines, `quantity` is what is **still to receive**, not what was ordered. `received_total` is what has arrived.
- The new status is a purchase order status id, from the status list in step 4. When the PO's warehouse holds stock, moving it to its final status needs every line except fees fully received first.

Each operation's reference page has its exact request and answer.

## Pitfalls

- **No create or receive.** Don't look for them in the public reference. They aren't there.
- **Links are for sales orders.** A quote can't be linked to a PO.
- **The PO id is its hash id.** A PO number such as `PO0042` isn't an id, and the export shows only numbers. See [IDs](https://developer.yoprint.com/concepts/ids.md).
- **An empty CSV export is an error.** A date range with no POs answers `422`, not an empty file (with `list_only: true` it returns an empty list).
- **Amounts are decimal strings or thousandths.** The PO report and exports return decimal strings (`"125.0000"` or `"120.00"`). The Zapier show operation returns amounts in thousandths (`10800` is 10.80). See [Money and quantities](https://developer.yoprint.com/concepts/money-and-quantities.md).
