# Search purchase orders

Operation ID: `searchPurchaseOrder`

Group: Accounting › Purchase Orders

[Open in the interactive reference](https://docs.api.lensing.ai/lensing/v1/endpoints/searchPurchaseOrder)

`POST /accounting/purchase-orders/search`

Read-only: A read that takes its filters in the body. Changes nothing, and ignores Idempotency-Key.

Searches purchase orders. Each match is the full order with its lines. The vendorId, vendorName, entityId, and entityName filters are available here and not on GET accounting/purchase-orders.

## Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `Api-Version` | header | string · v1 | yes | The API version this request targets, named by its public identifier. Required on every request; omitting it, or naming a version this company does not have, is answered with 400. This document describes version `v1`. |

## Request body

Required, `application/json`: searchPurchaseOrderInput.

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `filters` | object | no | The filters to apply. A purchase order must match every filter you send. Omit to return all purchase orders. Example: `{"approvalStatus":"pendingApproval"}`. |
| `filters.id` | string | no | Limits results to the purchase order with this identifier. GET accounting/purchase-orders/:id does the same more cheaply. |
| `filters.poNumber` | string | no | Limits results to the purchase order with this number, for example 'PO-3'. The match is exact. |
| `filters.externalPONumber` | string | no | Limits results to purchase orders with this external reference. The match is exact. It is not unique, so it can match several orders. |
| `filters.approvalStatus` | string | no | Limits results to orders in this approval status. Accepted values: 'approved', 'pendingApproval', 'rejected'. Any other value matches no purchase order. |
| `filters.budgetStatus` | string | no | Limits results to orders in this budget-check status. Accepted values: 'approved', 'rejected', 'pendingApproval'. Any other value matches no purchase order. |
| `filters.currency` | string | no | Limits results to orders denominated in this currency. Format ISO 4217, for example 'USD'. |
| `filters.paymentTerms` | string | no | Limits results to orders with this payment-terms code, for example 'net30'. The match is exact. |
| `filters.vendorId` | string | no | Limits results to orders placed with this vendor. See id on GET accounting/vendors/:id. An id that matches no vendor returns an empty page, not an error. |
| `filters.entityId` | string | no | Limits results to orders placed by this legal entity. See id on GET base/entities. An id that matches no entity returns an empty page, not an error. |
| `filters.vendorName` | string | no | Limits results to orders whose vendor name is exactly this. The match is case-sensitive. Use vendorId when you already have the id. |
| `filters.entityName` | string | no | Limits results to orders whose legal entity name is exactly this. The match is case-sensitive. Use entityId when you already have the id. |
| `page` | object | no | Pagination controls. Omit for the first page. Example: `{"size":50}`. |
| `page.cursor` | string | no | The cursor from the previous response's pagination.nextCursor. Omit on the first request. |
| `page.size` | number | no | The number of purchase orders per page, between 1 and 200. Defaults to 50. A value above 200 is treated as 200, not rejected. |

```json
{
  "filters": {
    "approvalStatus": "pendingApproval"
  },
  "page": {
    "size": 50
  }
}
```

## Response

`200` Success

Under `data` (searchPurchaseOrderOutput):

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `items` | array<object> | yes | The purchase orders on this page, most recently created first. An empty array when nothing matches. |
| `items[].id` | string | yes | The unique identifier for the purchase order. |
| `items[].poNumber` | string | no | The purchase order number, for example 'PO-3'. Absent while none has been assigned. |
| `items[].description` | string | no | The description of the order, shown to the vendor. |
| `items[].internalDescription` | string | no | Internal notes on the order, not shown to the vendor. |
| `items[].externalMemo` | string | no | A memo intended for the vendor. |
| `items[].externalPONumber` | string | no | The order reference in the vendor's or an upstream system. It is not unique. |
| `items[].approvalStatus` | string | no | The approval status of the order. Possible values: 'approved', 'pendingApproval', 'rejected'. |
| `items[].budgetStatus` | string | no | The budget-check status of the order. Possible values: 'approved', 'rejected', 'pendingApproval'. Independent of approvalStatus. |
| `items[].status` | string | no | The order lifecycle status. Possible values: 'open', 'inprogress', 'closed', 'overMatched'. This is not the vendor-bill matching status, which is per line in each line's matchStatus. |
| `items[].currency` | string | no | The currency the order is denominated in. Format ISO 4217, for example 'USD'. Governs every amount on the order and its lines. |
| `items[].totalAmount` | decimal | no | The total ordered amount, the sum of the line totals, in the order currency. |
| `items[].invoicedTotalAmount` | decimal | no | The portion of the order already invoiced by vendor bills, in the order currency. |
| `items[].receivedTotalAmount` | decimal | no | The portion of the order already received as goods, in the order currency. |
| `items[].startDate` | date | no | The first day of the period the order covers. Format YYYY-MM-DD. |
| `items[].endDate` | date | no | The last day of the period the order covers. Format YYYY-MM-DD. |
| `items[].needByDate` | date | no | The date the goods or services are needed by. Format YYYY-MM-DD. |
| `items[].paymentTerms` | string | no | The payment-terms code applied to this order, for example 'net30', 'net60', 'dueOnReceipt' or 'custom'. Inherited from the vendor unless overridden. |
| `items[].hasInventoryItems` | boolean | no | Whether any line references an inventory item. |
| `items[].vendorId` | string | no | The vendor this order is placed with. See id on GET accounting/vendors/:id. |
| `items[].vendorName` | string | no | The name of the vendor this order is placed with. |
| `items[].vendorContactEmail` | string | no | The main contact email at the vendor. |
| `items[].entityId` | string | no | The legal entity this order is placed by. See id on GET base/entities. |
| `items[].entityName` | string | no | The name of the legal entity this order is placed by. |
| `items[].lastModifiedDate` | date-time | yes | The date and time the purchase order last changed, in UTC. Format YYYY-MM-DDTHH:mm:ss.sssZ. |
| `items[].lines` | array<object> | no | The order line items, in their order on the order. An empty array when the order has none. |
| `items[].lines[].id` | string | yes | The unique identifier for the line item. |
| `items[].lines[].lineNumber` | string | no | The line number, for example 'POL-3'. |
| `items[].lines[].description` | string | no | A description of the line item. |
| `items[].lines[].isItem` | boolean | no | Whether the line is an item line. True when the line references an item, false when it is an expense line. |
| `items[].lines[].quantity` | decimal | no | The quantity ordered, as a decimal. |
| `items[].lines[].price` | decimal | no | The unit price, in the order currency. Set on item lines. On an expense line the amount is in editableAmount instead. |
| `items[].lines[].editableAmount` | decimal | no | The directly entered line amount, in the order currency. Used on expense lines, where there is no unit price to multiply. |
| `items[].lines[].total` | decimal | no | The line total, in the order currency: price times quantity on an item line, editableAmount on an expense line. |
| `items[].lines[].discountPercentage` | decimal | no | The discount applied to the line, as a percentage. 10 means 10%, not 0.10. |
| `items[].lines[].currency` | string | no | The currency of the line. Format ISO 4217, for example 'USD'. It always matches the order currency. |
| `items[].lines[].status` | string | no | The delivery status of the line. Possible values: 'undelivered', 'delivering', 'delivered'. |
| `items[].lines[].matchStatus` | string | no | The vendor-bill matching status of the line. Possible values: 'unmatched', 'partiallyMatched', 'matched', 'overMatched'. |
| `items[].lines[].invoicedStatus` | string | no | The invoicing status of the line. Possible values: 'uninvoiced', 'partiallyInvoiced', 'invoiced', 'overInvoiced'. |
| `items[].lines[].receivedStatus` | string | no | The goods-receipt status of the line. Possible values: 'unreceived', 'partiallyReceived', 'received', 'overReceived'. |
| `items[].lines[].invoicedQuantity` | decimal | no | The quantity invoiced against this line so far, as a decimal. |
| `items[].lines[].invoicedAmount` | decimal | no | The amount invoiced against this line so far, in the order currency. |
| `items[].lines[].receivedQuantity` | decimal | no | The quantity received against this line so far, as a decimal. |
| `items[].lines[].receivedAmount` | decimal | no | The amount received against this line so far, in the order currency. |
| `items[].lines[].remainingAmount` | decimal | no | The amount still open on this line, in the order currency: the line total less what has been invoiced. |
| `items[].lines[].startDate` | date | no | The first day of the period this line covers. Format YYYY-MM-DD. |
| `items[].lines[].endDate` | date | no | The last day of the period this line covers. Format YYYY-MM-DD. |
| `items[].lines[].deliveryDate` | date | no | The date this line was delivered. Format YYYY-MM-DD. |
| `items[].lines[].estimatedDeliveryDate` | date | no | The expected delivery date for this line. Format YYYY-MM-DD. |
| `items[].lines[].unitOfMeasure` | string | no | The name of the unit of measure for the quantity. |
| `items[].lines[].unitOfMeasureId` | string | no | The unit of measure for the quantity. |
| `items[].lines[].itemId` | string | no | The item this line orders. Present on item lines. |
| `items[].lines[].departmentId` | string | no | The department this line is charged to. See id on GET people/departments/:id. |
| `pagination` | object | yes | The pagination details for this page. |
| `pagination.nextCursor` | string | no | The cursor for the next page. Pass it as page.cursor. Absent on the last page. |
| `pagination.hasMore` | boolean | yes | Whether more matches remain beyond this page. |

```json
{
  "data": {
    "items": [
      {
        "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
        "poNumber": "<poNumber>",
        "description": "<description>",
        "internalDescription": "<internalDescription>",
        "externalMemo": "<externalMemo>",
        "externalPONumber": "<externalPONumber>",
        "approvalStatus": "<approvalStatus>",
        "budgetStatus": "<budgetStatus>",
        "status": "<status>",
        "currency": "<currency>",
        "totalAmount": "19.99",
        "invoicedTotalAmount": "19.99",
        "receivedTotalAmount": "19.99",
        "startDate": "2026-01-15",
        "endDate": "2026-01-15",
        "needByDate": "2026-01-15",
        "paymentTerms": "<paymentTerms>",
        "hasInventoryItems": true,
        "vendorId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
        "vendorName": "<vendorName>",
        "vendorContactEmail": "<vendorContactEmail>",
        "entityId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
        "entityName": "<entityName>",
        "lastModifiedDate": "2026-01-15T09:30:00.000Z",
        "lines": [
          {
            "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
            "lineNumber": "<lineNumber>",
            "description": "<description>",
            "isItem": true,
            "quantity": "19.99",
            "price": "19.99",
            "editableAmount": "19.99",
            "total": "19.99",
            "discountPercentage": "19.99",
            "currency": "<currency>",
            "status": "<status>",
            "matchStatus": "<matchStatus>",
            "invoicedStatus": "<invoicedStatus>",
            "receivedStatus": "<receivedStatus>",
            "invoicedQuantity": "19.99",
            "invoicedAmount": "19.99",
            "receivedQuantity": "19.99",
            "receivedAmount": "19.99",
            "remainingAmount": "19.99",
            "startDate": "2026-01-15",
            "endDate": "2026-01-15",
            "deliveryDate": "2026-01-15",
            "estimatedDeliveryDate": "2026-01-15",
            "unitOfMeasure": "<unitOfMeasure>",
            "unitOfMeasureId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
            "itemId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
            "departmentId": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
          }
        ]
      }
    ],
    "pagination": {
      "nextCursor": "<nextCursor>",
      "hasMore": true
    }
  },
  "diagnostics": [
    {
      "code": "<code>",
      "message": "<message>",
      "severity": "error",
      "path": [
        "<path>"
      ],
      "details": "<details>"
    }
  ]
}
```

## Statuses

- `400` — Invalid request or business failure
- `401` — The request carries no valid access token.
- `403` — Not authorized
- `404` — Not found
- `429` — Too many requests from this client address. The response has no body and no `Retry-After` header.
- `500` — Internal server error
