# Get a purchase order

```http
GET https://secure.yoprint.com/v1/api/store/{team_slug}/purchase_order/{hash_id}
```

- API version: v1
- Operation ID: `v1_public_purchase_order_show`
- Section: Purchasing › [Purchase orders](https://developer.yoprint.com/reference/purchase-orders.md)
- Web page: https://developer.yoprint.com/reference/purchase-orders/v1-purchase-order-show/
- Authentication: `Authorization: Bearer YOUR_API_KEY`

Returns one purchase order with its lines, status, vendor, delivery and billing addresses, creator and tags.

This operation is kept for YoPrint's Zapier app, which uses this path, and its answer keeps the flat line fields Zapier reads: each line in `items` has `product` (`type`, with `id` or `name`), `quantity`, `price` and totals as plain values. The public API has no newer purchase order operations to use instead: creating, editing and receiving purchase orders are not part of it.

Money in this answer is in **thousandths**, an older encoding (`10800` is 10.80): `calc_subtotal`, `calc_tax` and `calc_total` on the purchase order and on each line, and each line's `price`. A whole number of thousandths is a JSON integer; an amount finer than a thousandth comes back as a decimal string such as `"12656.3"`, whatever headers you send, so parse these as decimals. See [Money and quantities](/concepts/money-and-quantities/). A line's `quantity` is what is **still to receive**, not what was ordered; `received_total` is what has arrived. `items` lists every line, deleted and voided ones included, and the rows don't say which.

`{hash_id}` is the purchase order's `id`, not its number (`PO0042`). Deleted purchase orders are not found.

## Permission

Who can read it: the team owner; a role whose Manage Purchase Order level includes `view_all_po`, for any of the team's purchase orders; or one with `view_self_po`, only for purchase orders the API key's user created or is assigned to (any other purchase order answers 404).

## Path parameters

- `team_slug` (string, required): Your team slug, shown as Team ID next to your API key in Profile. Example `your-team`.
- `hash_id` (string, required): The purchase order's id (not its number, such as `PO0042`). A hash id from an earlier answer; unique keys and document numbers (such as `PO0042`) don't work here. Example `EDhVUaC6jbLbed`.

## Request body

This operation takes no request body.

### Example request

```bash
curl 'https://secure.yoprint.com/v1/api/store/your-team/purchase_order/EDhVUaC6jbLbed' \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -H 'Accept: application/json' \
  -H 'X-Yoprint-Decimal-Format: exact-v1'
```

## Response

`200` Successful response.

Fields:

- `data` (object, required)
  - `calc_subtotal` (integer | string, required): The purchase order's subtotal. Thousandths, an older encoding: `10800` is 10.80. A whole number of thousandths comes back as a JSON integer; an amount finer than a thousandth comes back as a decimal string, such as `"12656.3"`, whatever headers you send. Parse it as a decimal. See [Money and quantities](/concepts/money-and-quantities/).
  - `calc_tax` (integer | string, required): Its tax. Thousandths, an older encoding: `10800` is 10.80. A whole number of thousandths comes back as a JSON integer; an amount finer than a thousandth comes back as a decimal string, such as `"12656.3"`, whatever headers you send. Parse it as a decimal. See [Money and quantities](/concepts/money-and-quantities/).
  - `calc_total` (integer | string, required): Its total. Thousandths, an older encoding: `10800` is 10.80. A whole number of thousandths comes back as a JSON integer; an amount finer than a thousandth comes back as a decimal string, such as `"12656.3"`, whatever headers you send. Parse it as a decimal. See [Money and quantities](/concepts/money-and-quantities/).
  - `id` (string, required): The purchase order's id: use it in the path.
  - `items` (array of objects, required): Every line of the purchase order, including deleted and voided ones: the rows don't say which.
    - Each item:
      - `calc_subtotal` (integer | string, required): The line's subtotal as YoPrint stores it: usually `price` times the quantity ordered; on some older lines, times the quantity still to receive. Thousandths, an older encoding: `10800` is 10.80. A whole number of thousandths comes back as a JSON integer; an amount finer than a thousandth comes back as a decimal string, such as `"12656.3"`, whatever headers you send. Parse it as a decimal. See [Money and quantities](/concepts/money-and-quantities/).
      - `calc_tax` (integer | string, required): The line's tax. Thousandths, an older encoding: `10800` is 10.80. A whole number of thousandths comes back as a JSON integer; an amount finer than a thousandth comes back as a decimal string, such as `"12656.3"`, whatever headers you send. Parse it as a decimal. See [Money and quantities](/concepts/money-and-quantities/).
      - `calc_total` (integer | string, required): `calc_subtotal` plus `calc_tax`. Thousandths, an older encoding: `10800` is 10.80. A whole number of thousandths comes back as a JSON integer; an amount finer than a thousandth comes back as a decimal string, such as `"12656.3"`, whatever headers you send. Parse it as a decimal. See [Money and quantities](/concepts/money-and-quantities/).
      - `id` (string, required): The line's id.
      - `non_inventory` (boolean, required): true for a line that doesn't track stock.
      - `price` (integer | string, required): The unit price. Thousandths, an older encoding: `10800` is 10.80. A whole number of thousandths comes back as a JSON integer; an amount finer than a thousandth comes back as a decimal string, such as `"12656.3"`, whatever headers you send. Parse it as a decimal. See [Money and quantities](/concepts/money-and-quantities/).
      - `product` (object, required): What the line buys, flat. `id` is a catalog item id such as `smr-10001` for `db_catalog`, or the hash id of your product or variant for `db_simple`, `user_simple` and `db_variant`; `name` is the line's own name for `custom`, `fee`, `fees` and `customer_provide_product`. Older lines can have `cart_item` or `order_item`, with that record's id. `size` and `color` appear only when the line has them.
        - `type` (string, required): The product kind, such as `db_catalog`, `user_simple`, `db_variant`, `custom` or `fee`.
        - `color` (string, optional)
        - `id` (string, optional)
        - `name` (string | null, optional)
        - `size` (string, optional)
      - `quantity` (integer | string, required): The quantity **still to receive** (ordered minus received), not the quantity ordered. A whole number is a JSON integer; a fraction is a decimal string, such as `"1.25"`.
      - `received_total` (integer | string, required): The quantity received so far. A whole number is a JSON integer; a fraction is a decimal string, such as `"1.25"`.
      - `tax_total` (integer | string, required): The line's own tax rate, used when `tax_mode` is `line_level`, as a percentage ×1000: `8000` is 8% (0 when the line has none). A rate that is not a whole number comes back as a decimal string.
      - `position` (integer | string | null, optional): The line's place on the purchase order, or null. A whole number is a JSON integer; a fraction is a decimal string, such as `"1.25"`.
  - `scoped_id` (string, required): The purchase order number people see, such as `PO0042`. Not an id.
  - `status_id` (string | null, required)
  - `trashed` (boolean, required)
  - `billing_address` (object | null, optional): The billing address.
    - `address1` (string | null, required)
    - `id` (string, required)
    - `is_current_source` (boolean, required): False for deleted, replaced, Shopify or redacted addresses. Only current addresses can be picked for new documents or edited.
    - `is_primary` (boolean, required)
    - `label` (string | null, required)
    - `revision_number` (integer, required): Goes up by one on every saved change. Send it back as `base_revision_number` when updating the address to protect against concurrent edits.
    - `role` (string, required): `billing` or `shipping` for customer addresses.
    - `trashed` (boolean, required)
    - `address2` (string | null, optional)
    - `address_family_id` (string | null, optional): Shared by an address and the copies made when it was edited after use.
    - `city` (string | null, optional)
    - `copied_from_address_id` (string | null, optional)
    - `country` (string | null, optional)
    - `country_code` (string | null, optional)
    - `customer_id` (string, optional): Customer addresses only: the customer's id.
    - `external_type` (string | null, optional): `shopify` for an address synced from Shopify.
    - `hold_stock` (boolean, optional): Company locations only; false for customer addresses.
    - `is_default_warehouse` (boolean, optional): Company locations only; false for customer addresses.
    - `is_external` (boolean, optional): True for an address synced from Shopify; such addresses are read-only.
    - `nexus` (boolean, optional): Company locations only; false for customer addresses.
    - `note` (string | null, optional)
    - `owner_id` (string | null, optional): The owner's id: for a customer address, the customer's id (the same as `customer_id`).
    - `owner_type` (string, optional): `customer` for customer addresses. One of: `"customer"`, `"team"`, `"unknown"`.
    - `postal_can_validate` (boolean, optional)
    - `postal_is_valid` (boolean, optional)
    - `postal_validated` (boolean, optional): True once YoPrint has checked the address with its address validator. `postal_can_validate` says whether it could be checked and `postal_is_valid` gives the result. Any edit resets it to false.
    - `postcode` (string | null, optional)
    - `source` (string, optional): `customer` for customer addresses. One of: `"customer"`, `"company_location"`, `"custom"`, `"unknown"`.
    - `state` (string | null, optional)
    - `state_code` (string | null, optional)
    - `superseded_at` (string (date-time) | null, optional)
    - `superseded_by_address_id` (string | null, optional): Set on an old copy: the address that replaced it.
    - `used_by_active_profile` (boolean, optional): True when a brand profile uses it as Ship From. Sent on lists, reads, updates and customer answers; not on create or set-primary.
  - `billing_address_id` (string | null, optional)
  - `created_at` (string (date-time) | null, optional)
  - `created_by` (string | null, optional): The team member who created it.
  - `creator` (object | null, optional): The team member who created it: `id`, `name`, `email`, `avatar_url`, `timezone` and a few profile flags.
    - `email` (string | null, optional)
    - `id` (string, optional)
    - `name` (string | null, optional)
  - `doc_viewer_url` (string, optional): A link that opens the purchase order in the document viewer, with the same kind of token.
  - `due_date` (string (date-time) | null, optional)
  - `internal_note` (string | null, optional)
  - `is_custom_warehouse` (boolean, optional): true when the delivery address is not one of the team's company locations.
  - `issue_date` (string (date-time) | null, optional)
  - `name` (string | null, optional)
  - `pdf` (array of objects, optional): PDF links: the purchase order detail, its 4" x 6" label, and the team's custom purchase order templates. Each `url` carries a token like `pdf_url`.
    - Each item:
      - `description` (string | null, optional)
      - `label` (string, optional)
      - `url` (string, optional)
  - `pdf_url` (string, optional): A link that downloads the purchase order PDF. Its token stops working 9,000 hours (about a year) after the answer was made, and anyone with the link can open the PDF: don't share it.
  - `quickbook_disable_sync` (boolean, optional): true when QuickBooks sync is off for this purchase order.
  - `quickbook_last_error_sync` (string (date-time) | null, optional)
  - `quickbook_last_success_sync` (string (date-time) | null, optional)
  - `status` (object | null, optional): The purchase order's status, as List purchase order statuses returns it: `id`, `role` (`purchase_order`), `type` (`start`, `pre_production`, `in_progress` or `end`), `label`, `customer_label`, `color`, `position` and `trashed`.
    - `id` (string, optional)
    - `label` (string, optional)
    - `role` (string, optional)
    - `type` (string, optional): One of: `"start"`, `"pre_production"`, `"in_progress"`, `"end"`.
  - `status_completion_date` (string (date-time) | null, optional): When it was moved to its final (`end`) status; null otherwise.
  - `status_last_change_date` (string (date-time) | null, optional)
  - `tags` (array of objects, optional)
    - Each item:
      - `color_hex` (string | null, required): Such as `#2563EB`. Null for a tag saved without a color.
      - `id` (string, required)
      - `name` (string, required)
  - `tax_mode` (string | null, optional): `order_level`: one `tax_rate` for the purchase order, and each line is taxable or not. `line_level`: each line has its own rate (`tax_total`).
  - `tax_rate` (string | null, optional): For `order_level`: the rate as a decimal string fraction with 6 decimals, `"0.080000"` is 8%. Null when not set.
  - `team` (object, optional): Your team, as the shared purchase order answer carries it: `id`, `name`, `slug`, contact details, the public ids of its enabled payment gateways, and `plan`. Nothing here is specific to the purchase order.
  - `updated_at` (string (date-time) | null, optional)
  - `vendor` (object | null, optional): The vendor with its contacts, as Get a vendor returns it.
    - `id` (string, required)
    - `name` (string | null, required)
    - `trashed` (boolean, required): True when the vendor is deleted.
    - `account_number` (string | null, optional)
    - `contacts` (array of objects, optional): Every contact, deleted ones included.
      - Each item:
        - `created_at` (string (date-time), required)
        - `first_name` (string | null, required)
        - `id` (string, required)
        - `is_primary` (boolean, required)
        - `trashed` (boolean, required): True when the contact is deleted.
        - `updated_at` (string (date-time), required)
        - `customer_id` (string, optional): Customer contacts only.
        - `default_receive_sms` (boolean, optional)
        - `email` (string | null, optional)
        - `external_type` (string | null, optional)
        - `fax` (string | null, optional)
        - `fax_country` (string | null, optional)
        - `is_external` (boolean, optional): True for a contact synced from Shopify; such contacts are read-only.
        - `last_name` (string | null, optional)
        - `phone` (string | null, optional)
        - `phone_country` (string | null, optional)
        - `send_invoice` (boolean, optional): Customer contacts only.
        - `vendor_id` (string, optional): Vendor contacts only.
    - `default_vendor_note` (string | null, optional)
    - `internal_notes` (string | null, optional)
    - `website` (string | null, optional)
  - `vendor_id` (string | null, optional)
  - `vendor_note` (string | null, optional): The note printed for the vendor.
  - `version` (integer, optional): 2 for purchase orders made in the current editor; 1 for older ones.
  - `warehouse` (object | null, optional): The delivery address, usually one of the team's company locations. Same fields as `data.billing_address` above.
  - `warehouse_can_hold_stock` (boolean, optional): true when the delivery address is set and holds stock, so receiving adds stock there.
  - `warehouse_id` (string | null, optional): Where the goods are delivered.
- `status` (integer, required)
- `success` (boolean, required)

Example response:

```json
{
  "data": {
    "billing_address": {
      "address1": "100 Example Street",
      "address2": null,
      "address_family_id": null,
      "city": "Springfield",
      "copied_from_address_id": null,
      "country": "United States",
      "country_code": "US",
      "external_type": null,
      "hold_stock": true,
      "id": "GXCU6IMtXEV9Qn",
      "is_current_source": true,
      "is_default_warehouse": true,
      "is_external": false,
      "is_primary": true,
      "label": "Main warehouse",
      "nexus": true,
      "note": null,
      "owner_id": "g9NXwcqzTRIABH",
      "owner_type": "team",
      "postal_can_validate": false,
      "postal_is_valid": false,
      "postal_validated": false,
      "postcode": "62701",
      "revision_number": 1,
      "role": "store_location",
      "source": "company_location",
      "state": "Illinois",
      "state_code": "IL",
      "superseded_at": null,
      "superseded_by_address_id": null,
      "trashed": false
    },
    "billing_address_id": "GXCU6IMtXEV9Qn",
    "calc_subtotal": 120000,
    "calc_tax": 9600,
    "calc_total": 129600,
    "created_at": "2026-11-02T09:00:00+00:00",
    "created_by": "lOURnovd3hkIQI",
    "creator": {
      "avatar_url": null,
      "email": "alex@example.com",
      "id": "lOURnovd3hkIQI",
      "name": "Alex Rivera",
      "timezone": "America/Chicago"
    },
    "doc_viewer_url": "https://example.com/documents/purchase-order",
    "due_date": "2026-11-09T00:00:00+00:00",
    "id": "12vR7n9WUYn1CJ",
    "internal_note": null,
    "is_custom_warehouse": false,
    "issue_date": "2026-11-02T00:00:00+00:00",
    "items": [
      {
        "calc_subtotal": 108000,
        "calc_tax": 8640,
        "calc_total": 116640,
        "id": "szhbjt6FGUoOcW",
        "non_inventory": false,
        "position": 1,
        "price": 4500,
        "product": {
          "color": "Black",
          "id": "smr-10001",
          "size": "L",
          "type": "db_catalog"
        },
        "quantity": 24,
        "received_total": 0,
        "tax_total": 0
      },
      {
        "calc_subtotal": 12000,
        "calc_tax": 960,
        "calc_total": 12960,
        "id": "gJn3IswvuEsoUd",
        "non_inventory": false,
        "position": 2,
        "price": 12000,
        "product": {
          "name": "Freight",
          "type": "fee"
        },
        "quantity": 1,
        "received_total": 0,
        "tax_total": 0
      }
    ],
    "name": "Fall restock",
    "pdf": [
      {
        "description": "Purchase Order Detail",
        "label": "Purchase Order Detail",
        "url": "https://example.com/pdf/purchase-order.pdf"
      },
      {
        "description": "Purchase Order Label",
        "label": "Purchase Order Label (4\" x 6\")",
        "url": "https://example.com/pdf/purchase-order-label.pdf"
      }
    ],
    "pdf_url": "https://example.com/pdf/purchase-order.pdf",
    "quickbook_disable_sync": false,
    "quickbook_last_error_sync": null,
    "quickbook_last_success_sync": null,
    "scoped_id": "PO0042",
    "status": {
      "color": "#2196f3",
      "customer_label": "In Progress",
      "id": "pBxSsJQFExGsC5",
      "label": "Ordered",
      "position": 2,
      "role": "purchase_order",
      "trashed": false,
      "type": "in_progress"
    },
    "status_completion_date": null,
    "status_id": "pBxSsJQFExGsC5",
    "status_last_change_date": "2026-11-03T09:00:00+00:00",
    "tags": [],
    "tax_mode": "order_level",
    "tax_rate": "0.080000",
    "team": {
      "id": "g9NXwcqzTRIABH",
      "name": "Example Print Shop",
      "payment_gateway": [],
      "slug": "your-team",
      "timezone": "America/Chicago"
    },
    "trashed": false,
    "updated_at": "2026-11-03T09:00:00+00:00",
    "vendor": {
      "account_number": "ACCT-1001",
      "contacts": [
        {
          "created_at": "2026-11-01T09:00:00+00:00",
          "default_receive_sms": false,
          "email": "sam@example.com",
          "external_type": null,
          "fax": null,
          "fax_country": null,
          "first_name": "Sam",
          "id": "fopJXRDkZ8Qk7S",
          "is_external": false,
          "is_primary": true,
          "last_name": "Lee",
          "phone": "+1 201 555 0123",
          "phone_country": "US",
          "trashed": false,
          "updated_at": "2026-11-01T09:00:00+00:00",
          "vendor_id": "MFwvMEIK7ud89l"
        }
      ],
      "default_vendor_note": null,
      "id": "MFwvMEIK7ud89l",
      "internal_notes": null,
      "name": "Example Blanks Co",
      "trashed": false,
      "website": "https://example.com"
    },
    "vendor_id": "MFwvMEIK7ud89l",
    "vendor_note": "Deliver to the back dock.",
    "version": 2,
    "warehouse": {
      "address1": "100 Example Street",
      "address2": null,
      "address_family_id": null,
      "city": "Springfield",
      "copied_from_address_id": null,
      "country": "United States",
      "country_code": "US",
      "external_type": null,
      "hold_stock": true,
      "id": "GXCU6IMtXEV9Qn",
      "is_current_source": true,
      "is_default_warehouse": true,
      "is_external": false,
      "is_primary": true,
      "label": "Main warehouse",
      "nexus": true,
      "note": null,
      "owner_id": "g9NXwcqzTRIABH",
      "owner_type": "team",
      "postal_can_validate": false,
      "postal_is_valid": false,
      "postal_validated": false,
      "postcode": "62701",
      "revision_number": 1,
      "role": "store_location",
      "source": "company_location",
      "state": "Illinois",
      "state_code": "IL",
      "superseded_at": null,
      "superseded_by_address_id": null,
      "trashed": false
    },
    "warehouse_can_hold_stock": true,
    "warehouse_id": "GXCU6IMtXEV9Qn"
  },
  "status": 200,
  "success": true
}
```

## Errors

- `401` The API key is missing or wrong (`unauthenticated`).
- `403` You are not allowed to do this. `api_access_not_in_plan`: your team's plan doesn't include API access (Pro and above, with an active subscription or trial), so every request answers this until the team upgrades. When your role lacks the permission the operation needs, the body is only `{"message": "Insufficient Permission"}`. Other refusals use the error envelope, with `unauthorized` or `user_is_not_authorized`.
  - When: Your role has no Manage Purchase Order permission to view. This 403 has a short body, `{"message": "Insufficient Permission"}`, with no `success` or `error`. Fix: Ask a team owner to give your role a Manage Purchase Order level.
- `404` Not found. `page_not_found`: the team slug is unknown, you are not a member of the team, or an id in the path is not one of this team's. `code: null`, with a message that starts "No query results for": there is no record with that id.
  - When: No live purchase order with this id in the team, or your role sees only purchase orders you created or are assigned to and this is not one of them. `error.code` is null and `error.message` names the record type. Fix: Check the id, or ask to be assigned to the purchase order.

## Notes

- A line's `quantity` is the quantity still to receive. To get the quantity ordered, add `received_total`.
- Read `status.type`: `end` means the purchase order is complete.

## Related

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