# Search vendor bills

Operation ID: `searchVendorBill`

Group: Accounting › Vendor Bills

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

`POST /accounting/vendor-bills/search`

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

Searches vendor bills. Each match has the same fields GET accounting/vendor-bills/:id returns, with its line items and their amortization schedules, but without the bill's approval decisions.

The purchase-order filters match at line level. A bill matches when any of its lines is linked to a line of that purchase order.

## 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`: searchVendorBillInput.

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `filters` | object | no | The filters to apply. A vendor bill must match every filter you send. Omit to return all vendor bills. Example: `{"billNumber":"INV-88213"}`. |
| `filters.id` | string | no | Limits results to the vendor bill with this identifier. |
| `filters.vendorBillNumber` | string | no | Limits results to the vendor bill with this number. |
| `filters.billNumber` | string | no | Limits results to bills with this vendor-issued invoice number. It is not unique, so this can match several bills. |
| `filters.vendorId` | string | no | Limits results to bills from this vendor. See id on GET accounting/vendors/:id. |
| `filters.entityId` | string | no | Limits results to bills posted under this legal entity. See id on GET base/entities. |
| `filters.vendorName` | string | no | Limits results to bills from a vendor with this name. |
| `filters.entityName` | string | no | Limits results to bills posted under a legal entity with this name. |
| `filters.status` | string | no | Limits results to bills in this approval status. Values include 'pendingApproval', 'approved', 'rejected', 'voided' and 'reversed'. Any other value matches no bill. |
| `filters.paymentStatus` | string | no | Limits results to bills in this payment status. Values include 'unpaid', 'partiallyPaid', 'fullyPaid', 'pending', 'notApplicable' and 'voided'. Any other value matches no bill. |
| `filters.externalVendorBillId` | string | no | Limits results to bills with this upstream identifier. It is not unique, so this can match several bills. |
| `filters.purchaseOrderId` | string | no | Limits results to bills matched to this purchase order. See id on GET accounting/purchase-orders/:id. The match is line-level. |
| `filters.purchaseOrderNumber` | string | no | Limits results to bills matched to the purchase order with this number. The match is line-level. |
| `filters.externalPurchaseOrderNumber` | string | no | Limits results to bills matched to a purchase order with this external reference. It is not unique, and the match is line-level. |
| `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 for the first request. |
| `page.size` | number | no | The number of vendor bills per page. Defaults to 50. A value above 200 is treated as 200, not rejected. |

```json
{
  "filters": {
    "billNumber": "INV-88213"
  },
  "page": {
    "size": 50
  }
}
```

## Response

`200` Success

Under `data` (searchVendorBillOutput):

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `items` | array<object> | yes | The vendor bills that match, most recently created first. An empty array when nothing matches. |
| `items[].id` | string | yes | The unique identifier for the vendor bill. |
| `items[].vendorBillNumber` | string | yes | The vendor bill number. Stable across edits. |
| `items[].billNumber` | string | yes | The vendor-issued invoice number, as printed on the bill. It is not unique across vendors. |
| `items[].billDate` | date | yes | The date printed on the vendor's invoice. Format YYYY-MM-DD. |
| `items[].serviceDate` | date | no | The date the goods or services were delivered. Format YYYY-MM-DD. Absent when not recorded. |
| `items[].dueDate` | date | yes | The date payment is due. Format YYYY-MM-DD. |
| `items[].currency` | string | yes | The currency the bill is denominated in. Format ISO 4217, for example 'USD'. Governs total, amountPaid, openAmount and every line amount. |
| `items[].status` | string | yes | The approval status. Values include 'pendingApproval', 'approved', 'rejected', 'voided' and 'reversed'. |
| `items[].paymentStatus` | string | no | The payment status. Values include 'unpaid', 'partiallyPaid', 'fullyPaid', 'pending', 'notApplicable' and 'voided'. |
| `items[].postingStatus` | string | no | The accounting posting status, defined by the accounting subsystem, for example 'notPosted', 'posted' or 'postingFailed'. |
| `items[].paymentTerms` | string | no | The payment-terms key applied to this bill, for example 'net30', 'net60' or 'dueOnReceipt'. Inherited from the vendor unless overridden. |
| `items[].total` | decimal | no | The total bill amount, gross, in the bill currency. |
| `items[].amountPaid` | decimal | no | The cumulative amount paid against this bill so far, in the bill currency. |
| `items[].openAmount` | decimal | no | The outstanding amount still owed, total minus amountPaid, in the bill currency. |
| `items[].externalVendorBillId` | string | no | The identifier this bill has in the upstream system that produced it. It is not unique. |
| `items[].vendorId` | string | no | The vendor this bill is from. See id on GET accounting/vendors/:id. |
| `items[].vendorName` | string | no | The name of the vendor this bill is from. |
| `items[].entityId` | string | no | The legal entity this bill is posted under. See id on GET base/entities. |
| `items[].entityName` | string | no | The name of the legal entity this bill is posted under. |
| `items[].purchaseOrderId` | string | no | The purchase order this bill is matched to. See id on GET accounting/purchase-orders/:id. The match is line-level, so it is absent when the bill is matched to no purchase order. |
| `items[].purchaseOrderNumber` | string | no | The number of the purchase order this bill is matched to. Absent when the bill is matched to no purchase order. |
| `items[].externalPurchaseOrderNumber` | string | no | The external reference of the purchase order this bill is matched to. Absent when the bill is matched to no purchase order, or when that purchase order has no external reference. |
| `items[].lastModifiedDate` | date-time | yes | The date and time the vendor bill last changed, in UTC. Format YYYY-MM-DDTHH:mm:ss.sssZ. |
| `items[].origin` | string | no | The source system this record came from, as free-form text. Absent when none was supplied. |
| `items[].lines` | array<object> | no | The bill line items, in their order on the bill. An empty array when the bill has none. |
| `items[].lines[].id` | string | yes | The unique identifier for the line item. |
| `items[].lines[].lineNumber` | string | no | The line item number. |
| `items[].lines[].description` | string | no | A description of the line item, typically copied from the vendor's invoice. |
| `items[].lines[].lineType` | string | no | The type of line item. Values include 'expense' and 'prepaidExpense'. |
| `items[].lines[].amount` | decimal | no | The total line amount, gross, in the bill currency. |
| `items[].lines[].currency` | string | no | The currency for the line. It always matches the bill currency. Format ISO 4217, for example 'USD'. |
| `items[].lines[].netAmount` | decimal | no | The line amount excluding VAT, in the bill currency. |
| `items[].lines[].vatAmount` | decimal | no | The computed VAT amount on the line, in the bill currency. |
| `items[].lines[].vatPercentage` | decimal | no | The VAT rate applied, as a percentage. 21 means 21%, not 0.21. |
| `items[].lines[].billableExpense` | string | no | The billable-expense status for this line. Values include 'yes', 'no' and 'skipped'. |
| `items[].lines[].externalVendorBillItemId` | string | no | The identifier this line has in the upstream system that produced it. |
| `items[].lines[].accountId` | string | no | The GL expense account this line posts to. See id on GET accounting/accounts/:id. |
| `items[].lines[].departmentId` | string | no | The department this line is charged to. See id on GET people/departments/:id. |
| `items[].lines[].crossChargeEntityId` | string | no | The legal entity this line is cross charged to (Charge to). See id on GET base/entities. Absent when the line is not cross charged. |
| `items[].lines[].amortizationSchedules` | array<object> | no | The amortization schedules this line is deferred over, one per accounting book. Absent when the line is not amortized, which is every line whose lineType is not 'prepaidExpense'. Read the scheduled installments with GET accounting/amortization-schedule-lines, filtering on amortizationScheduleHeaderId. |
| `items[].lines[].amortizationSchedules[].id` | string | yes | The unique identifier for the amortization schedule. Pass it as amortizationScheduleHeaderId to GET accounting/amortization-schedule-lines to read its installments. |
| `items[].lines[].amortizationSchedules[].bookId` | string | no | The accounting book this schedule posts into. It is what distinguishes the entries of this array. See id on GET accounting/books/:id. |
| `items[].lines[].amortizationSchedules[].bookName` | string | no | The name of the accounting book this schedule posts into. |
| `items[].lines[].amortizationSchedules[].status` | string | no | How far the schedule has progressed. Values include 'notStarted', 'inProgress', 'completed' and 'voided'. A schedule created alongside an unposted bill reads 'notStarted' until the bill posts. |
| `items[].lines[].amortizationSchedules[].amortizationMethod` | string | no | How the schedule spreads its total. Values include 'exactDays', 'pointInTime', 'straightLine', 'straightLineProRateFirstLast' and 'usageBased'. |
| `items[].lines[].amortizationSchedules[].amortizationStartDate` | date | no | The first day the schedule amortizes over. Format YYYY-MM-DD. |
| `items[].lines[].amortizationSchedules[].amortizationEndDate` | date | no | The last day the schedule amortizes over, inclusive. Format YYYY-MM-DD. |
| `items[].lines[].amortizationSchedules[].term` | decimal | no | The number of months the schedule spreads over. A count, not an amount, so it carries no currency. |
| `items[].lines[].amortizationSchedules[].description` | string | no | The note recorded against the schedule, where one was given. |
| `items[].lines[].amortizationSchedules[].deferredAccountId` | string | no | The general ledger account the deferred balance sits on until it is recognized. See id on GET accounting/accounts/:id. |
| `items[].lines[].amortizationSchedules[].recognitionAccountId` | string | no | The general ledger account each installment recognizes the expense into. See id on GET accounting/accounts/:id. It is taken from the schedule's installments, so it is absent on a schedule that has none yet. |
| `items[].lines[].amortizationSchedules[].totalRecognized` | decimal | no | The amount recognized so far across the schedule's posted installments, in the bill currency. Zero until the first installment posts. |
| `pagination` | object | yes | The pagination cursor 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 | True when more bills remain beyond this page. |

```json
{
  "data": {
    "items": [
      {
        "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
        "vendorBillNumber": "<vendorBillNumber>",
        "billNumber": "<billNumber>",
        "billDate": "2026-01-15",
        "serviceDate": "2026-01-15",
        "dueDate": "2026-01-15",
        "currency": "<currency>",
        "status": "<status>",
        "paymentStatus": "<paymentStatus>",
        "postingStatus": "<postingStatus>",
        "paymentTerms": "<paymentTerms>",
        "total": "19.99",
        "amountPaid": "19.99",
        "openAmount": "19.99",
        "externalVendorBillId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
        "vendorId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
        "vendorName": "<vendorName>",
        "entityId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
        "entityName": "<entityName>",
        "purchaseOrderId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
        "purchaseOrderNumber": "<purchaseOrderNumber>",
        "externalPurchaseOrderNumber": "<externalPurchaseOrderNumber>",
        "lastModifiedDate": "2026-01-15T09:30:00.000Z",
        "origin": "<origin>",
        "lines": [
          {
            "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
            "lineNumber": "<lineNumber>",
            "description": "<description>",
            "lineType": "<lineType>",
            "amount": "19.99",
            "currency": "<currency>",
            "netAmount": "19.99",
            "vatAmount": "19.99",
            "vatPercentage": "19.99",
            "billableExpense": "<billableExpense>",
            "externalVendorBillItemId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
            "accountId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
            "departmentId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
            "crossChargeEntityId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
            "amortizationSchedules": [
              null
            ]
          }
        ]
      }
    ],
    "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
