# Create a purchase return

Operation ID: `createPurchaseReturn`

Group: Inventory › Purchase Returns

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

`POST /inventory/purchase-returns`

Retry-safe: A write that accepts Idempotency-Key: repeating a request with the same key replays the stored answer instead of executing twice.

Creates a purchase return with the `documentStatus` 'DRAFT'. The response contains the whole purchase return.

To create a purchase return for everything still returnable on a purchase order, use POST inventory/purchase-returns/from-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`. |
| `Idempotency-Key` | header | string | no | An opaque caller-chosen string (1-255 characters) that makes this request safe to retry. Repeating the same key with an identical request body replays the original response instead of re-executing - but only once that original request succeeded; a failed attempt re-executes on retry. Repeating the key with a different body is answered with 422. A key still in flight is answered with 409 - as is a key whose original request was interrupted before it finished, for a few minutes afterwards, until that attempt is presumed dead. A completed response stays replayable for 24 hours. The key is scoped to the caller, not to the action: reusing it for a different action is answered with 409 or 422 rather than treated independently. On read actions this header is ignored. On write actions that cannot offer the guarantee it is answered with `400 IDEMPOTENCY_NOT_SUPPORTED` rather than silently ignored. |

## Request body

Required, `application/json`: createPurchaseReturnInput.

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `businessPartnerId` | string | yes | The unique identifier of the vendor the goods go back to. Format: UUID. See `id` on GET accounting/vendors/:id (getVendor). Example: `"b62e4f17-0c93-4a58-8d21-5f70e3b9a4c6"`. |
| `entityId` | string | no | The unique identifier of the legal entity the purchase return belongs to. Format: UUID. See `id` on GET base/entities (entities). |
| `documentDate` | date | no | The date of the purchase return. Format: YYYY-MM-DD. Defaults to today's date in UTC. It cannot be in the future. Example: `"2026-08-20"`. |
| `currency` | string | no | The currency of the purchase return's amounts. Format: ISO 4217, for example 'EUR'. If this currency differs from the legal entity's functional currency, an exchange rate between the two currencies must exist for the `documentDate`. |
| `returnType` | string | no | The type of the purchase return. Defaults to 'NORMAL'. 'NORMAL' means the vendor is expected to credit the goods. 'UNCREDITED_REPLACEMENT' means the vendor sends replacement goods, and the quantity can be received again on the purchase order line. 'UNINVOICED_CANCELLATION' means the quantity is canceled from the purchase order before it is invoiced. Shipping an 'UNINVOICED_CANCELLATION' purchase return fails when a line returns more of its purchase order line than is received, not invoiced, and not already canceled. Accepted values: 'NORMAL', 'UNCREDITED_REPLACEMENT', 'UNINVOICED_CANCELLATION'. Example: `"NORMAL"`. |
| `originalReceiptId` | string | no | The unique identifier of the receipt the purchase return is created against. Format: UUID. The receipt must belong to the purchase return's legal entity. Setting it does not fill in the lines. Each line still names the receipt line or purchase order line it returns goods from, in `sourceDocumentLineId`. See `id` on GET inventory/receipts/:id (getReceipt). |
| `referenceNumber` | string | no | The reference number of the purchase return. Example: `"RMA-4471"`. |
| `notes` | string | no | Notes on the purchase return. Example: `"Damaged in transit"`. |
| `lines` | array<object> | yes | The lines of the purchase return. Send at least one. Example: `[{"itemId":"5f21c8b0-9d34-4a76-8e12-04b93c7de5a8","locationId":"e1d84c07-5b39-4a26-9f70-2c8b613ad495","unitId":"3b7d5e9c-1a24-4f80-9c6d-2e8f70b13a45","quantity":"30.00","unitPrice":"3.20","sourceDocumentLineId":"6b02f4d8-91c3-4e57-a8f6-3c1d9e70b254","sourceDocumentLineType":"InvPurchaseDeliveryLine","notes":"Cracked casings"}]`. |
| `lines[].itemId` | string | yes | The unique identifier of the item being returned. Format: UUID. See `id` on GET inventory/items/:id (getItem). |
| `lines[].locationId` | string | yes | The unique identifier of the warehouse that the stock on this line leaves from. Format: UUID. It must belong to the purchase return's legal entity. See `id` on GET inventory/warehouses/:id (getWarehouse). |
| `lines[].unitId` | string | no | The unique identifier of the unit of measure for the line's quantity. Format: UUID. Required. When the line returns goods from a receipt line ('InvPurchaseDeliveryLine'), it must be the same unit as on the receipt line. See `id` on GET inventory/unit-of-measures/:id (getUnitOfMeasure). |
| `lines[].quantity` | decimal | yes | The quantity to return, in the line's unit of measure. It must be greater than zero. |
| `lines[].unitPrice` | decimal | no | The price of one unit, in the purchase return's currency. It can be zero, but not less than zero. |
| `lines[].sourceDocumentLineId` | string | no | The identifier of the receipt line or purchase order line that this line returns goods from. Send the line's unique identifier or its numeric `id`. Set `sourceDocumentLineType` to say which kind of line it is. See `lines[].id` on GET inventory/receipts/:id (getReceipt) or GET accounting/purchase-orders/:id (getPurchaseOrder). |
| `lines[].sourceDocumentLineType` | string | no | The type of line that `sourceDocumentLineId` points to. Defaults to 'InvPurchaseDeliveryLine'. Accepted values: 'InvPurchaseDeliveryLine' (a receipt line), 'PurchaseOrderLine' (a purchase order line). |
| `lines[].notes` | string | no | Notes on the line. |
| `lines[].batches` | array<object> | no | The batches being returned. Required when the item is batch-tracked. |
| `lines[].batches[].batchNumberId` | string | no | The unique identifier of the batch being returned. Format: UUID. See `id` on GET inventory/batch-numbers/:id (getBatchNumber). |
| `lines[].batches[].batchNumber` | string | no | The batch number of the batch being returned. To identify the batch, send `batchNumberId`. To take this batch from a bin, send the same value in `binLocations[].batchNumber`. |
| `lines[].batches[].quantity` | decimal | yes | The quantity of the batch to return, in the item's stocking unit. It must be greater than zero. |
| `lines[].serials` | array<object> | no | The serialized units being returned, one entry for each unit. Required when the item is serialized. |
| `lines[].serials[].serialNumberId` | string | no | The unique identifier of the serialized unit being returned. Format: UUID. See `id` on GET inventory/serial-numbers/:id (getSerialNumber). |
| `lines[].serials[].serialNumber` | string | no | The serial number of the unit being returned. To identify the unit, send `serialNumberId`. To take this unit from a bin, send the same value in `binLocations[].serialNumber`. |
| `lines[].binLocations` | array<object> | no | The bins to take the stock on this purchase return line from, with the quantity to take from each bin. An entry with a `batchNumber` or `serialNumber` is for that batch or serialized unit. An entry with neither is for part of the line's quantity, not a particular batch or serialized unit. |
| `lines[].binLocations[].binLocationId` | string | yes | The unique identifier of the bin the stock is taken from. Format: UUID. See `id` on GET inventory/bins/:id (getBin). |
| `lines[].binLocations[].quantity` | decimal | yes | The quantity taken from the bin, in the item's stocking unit. |
| `lines[].binLocations[].batchNumber` | string | no | The batch number of the batch this bin quantity is for. It must match the `batchNumber` of one of the line's batches. |
| `lines[].binLocations[].serialNumber` | string | no | The serial number of the unit this bin quantity is for. It must match the `serialNumber` of one of the line's serials. |

```json
{
  "businessPartnerId": "b62e4f17-0c93-4a58-8d21-5f70e3b9a4c6",
  "entityId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "documentDate": "2026-08-20",
  "currency": "<currency>",
  "returnType": "NORMAL",
  "originalReceiptId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "referenceNumber": "RMA-4471",
  "notes": "Damaged in transit",
  "lines": [
    {
      "itemId": "5f21c8b0-9d34-4a76-8e12-04b93c7de5a8",
      "locationId": "e1d84c07-5b39-4a26-9f70-2c8b613ad495",
      "unitId": "3b7d5e9c-1a24-4f80-9c6d-2e8f70b13a45",
      "quantity": "30.00",
      "unitPrice": "3.20",
      "sourceDocumentLineId": "6b02f4d8-91c3-4e57-a8f6-3c1d9e70b254",
      "sourceDocumentLineType": "InvPurchaseDeliveryLine",
      "notes": "Cracked casings"
    }
  ]
}
```

## Response

`200` Success

Under `data` (createPurchaseReturnOutput):

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | string | yes | The unique identifier for the created purchase return. Format: UUID. Example: `"6a1f30c9-4d82-4b57-9e60-83c25f0a71b4"`. |
| `documentNumber` | string | no | The purchase return number. Example: `"PRTR-1043"`. |
| `documentStatus` | string | yes | The status of the purchase return. It is always 'DRAFT' on a created purchase return. Example: `"DRAFT"`. |
| `documentDate` | date | yes | The date of the purchase return. Format: YYYY-MM-DD. Example: `"2026-08-20"`. |
| `deliveryDate` | date | no | The delivery date of the purchase return. Format: YYYY-MM-DD. |
| `returnType` | string | no | The type of the purchase return. 'NORMAL' means the vendor is expected to credit the goods. 'UNCREDITED_REPLACEMENT' means the vendor sends replacement goods, and the quantity can be received again on the purchase order line. 'UNINVOICED_CANCELLATION' means the quantity is canceled from the purchase order before it is invoiced. Possible values: 'NORMAL', 'UNCREDITED_REPLACEMENT', 'UNINVOICED_CANCELLATION'. Example: `"NORMAL"`. |
| `entityId` | string | yes | The unique identifier of the legal entity the purchase return belongs to. Format: UUID. See `id` on GET base/entities (entities). Example: `"1a9c4e70-38b5-4d26-9f01-7c2e5b8a6d43"`. |
| `entityName` | string | no | The name of the legal entity the purchase return belongs to. |
| `businessPartnerId` | string | yes | The unique identifier of the vendor the goods go back to. Format: UUID. See `id` on GET accounting/vendors/:id (getVendor). Example: `"b62e4f17-0c93-4a58-8d21-5f70e3b9a4c6"`. |
| `businessPartnerName` | string | no | The name of the vendor the goods go back to. |
| `referenceNumber` | string | no | The reference number of the purchase return. |
| `originalReceiptId` | string | no | The unique identifier of the receipt the purchase return was created against. Format: UUID. See `id` on GET inventory/receipts/:id (getReceipt). |
| `currency` | string | no | The currency of the purchase return's amounts. Format: ISO 4217. Example: `"EUR"`. |
| `totalAmount` | decimal | no | The total value of the purchase return, in its currency. It is the sum, over all lines, of each line's `unitPrice` multiplied by its `quantity`. Absent until the purchase return is shipped. |
| `notes` | string | no | Notes on the purchase return. |
| `createdDate` | date-time | no | The date and time the purchase return was created, in UTC. Format: YYYY-MM-DDTHH:mm:ss.sssZ. |
| `lastModifiedDate` | date-time | no | The date and time the purchase return last changed, in UTC. Format: YYYY-MM-DDTHH:mm:ss.sssZ. |
| `lines` | array<object> | yes | The lines of the purchase return. Example: `[{"id":"8c14a6f2-3b70-4d59-9e02-6f5a1d38b47c","itemId":"5f21c8b0-9d34-4a76-8e12-04b93c7de5a8","quantity":"30.00","unitPrice":"3.20"}]`. |
| `lines[].id` | string | yes | The unique identifier for the line. Format: UUID. Use it in `updateLines` and `deleteLines`. |
| `lines[].itemId` | string | no | The unique identifier of the item being returned. Format: UUID. See `id` on GET inventory/items/:id (getItem). |
| `lines[].itemCode` | string | no | The code of the item being returned. |
| `lines[].itemName` | string | no | The name of the item being returned. |
| `lines[].locationId` | string | no | The unique identifier of the warehouse that the stock on this line leaves from. Format: UUID. See `id` on GET inventory/warehouses/:id (getWarehouse). |
| `lines[].locationCode` | string | no | The code of the warehouse that the stock on this line leaves from. |
| `lines[].locationName` | string | no | The name of the warehouse that the stock on this line leaves from. |
| `lines[].unitId` | string | no | The unique identifier of the unit of measure for the line's quantity. Format: UUID. See `id` on GET inventory/unit-of-measures/:id (getUnitOfMeasure). |
| `lines[].unitCode` | string | no | The code of the unit of measure for the line's quantity. |
| `lines[].quantity` | decimal | no | The quantity being returned, in the line's unit of measure. |
| `lines[].inventoryQuantity` | decimal | no | The quantity being returned, in the item's stocking unit. |
| `lines[].openQuantity` | decimal | no | The quantity of the line still outstanding, in the line's unit of measure. |
| `lines[].unitPrice` | decimal | no | The price of one unit, in the purchase return's currency. |
| `lines[].functionalUnitPrice` | decimal | no | The price of one unit, in the legal entity's functional currency. It is `functionalLineTotal` divided by `quantity`. Absent until the purchase return is shipped, and on lines whose `sourceDocumentLineType` is not 'PurchaseOrderLine'. |
| `lines[].functionalLineTotal` | decimal | no | The total of the line, in the legal entity's functional currency. It is calculated from the receipts the quantity was taken from, at their original prices. Absent until the purchase return is shipped, and on lines whose `sourceDocumentLineType` is not 'PurchaseOrderLine'. |
| `lines[].status` | string | no | The status of the line. It is 'PENDING' until the purchase return is shipped, and 'SHIPPED' after. |
| `lines[].sourceDocumentLineId` | string | no | The unique identifier of the receipt line or purchase order line that this line returns goods from. Format: UUID. `sourceDocumentLineType` says whether it is a receipt line or a purchase order line. See `lines[].id` on GET inventory/receipts/:id (getReceipt) or GET accounting/purchase-orders/:id (getPurchaseOrder). |
| `lines[].sourceDocumentLineType` | string | no | The type of line that `sourceDocumentLineId` points to. Possible values: 'InvPurchaseDeliveryLine' (a receipt line), 'PurchaseOrderLine' (a purchase order line). |
| `lines[].notes` | string | no | Notes on the line. |
| `lines[].batches` | array<object> | no | The batches being returned. |
| `lines[].batches[].batchNumberId` | string | no | The unique identifier of the batch being returned. Format: UUID. See `id` on GET inventory/batch-numbers/:id (getBatchNumber). |
| `lines[].batches[].batchNumber` | string | yes | The batch number of the batch being returned. |
| `lines[].batches[].expirationDate` | date | no | The date the batch expires. Format: YYYY-MM-DD. See `expirationDate` on GET inventory/batch-numbers/:id (getBatchNumber). |
| `lines[].batches[].quantity` | decimal | yes | The quantity of the batch being returned, in the item's stocking unit. |
| `lines[].batches[].inventoryQuantity` | decimal | no | The quantity of the batch being returned, in the item's stocking unit. |
| `lines[].batches[].unitCost` | decimal | no | The purchase return line's `unitPrice` when the batch was added to that line, in the purchase return's currency. It is per unit of the line's unit of measure, not per stocking unit like `quantity`. |
| `lines[].serials` | array<object> | no | The serialized units being returned, one entry for each unit. |
| `lines[].serials[].serialNumberId` | string | no | The unique identifier of the serialized unit being returned. Format: UUID. See `id` on GET inventory/serial-numbers/:id (getSerialNumber). |
| `lines[].serials[].serialNumber` | string | yes | The serial number of the unit being returned. |
| `lines[].serials[].unitCost` | decimal | no | The cost of the serialized unit, in the purchase return's currency. It is the purchase return line's `unitPrice` when the serialized unit was added to that line. |
| `lines[].binLocations` | array<object> | no | The bins the stock on this purchase return line is taken from, with the quantity taken from each bin. An entry with a `batchNumber` or `serialNumber` is for that batch or serialized unit. An entry with neither is for part of the line's quantity, not a particular batch or serialized unit. |
| `lines[].binLocations[].binLocationId` | string | yes | The unique identifier of the bin the stock is taken from. Format: UUID. See `id` on GET inventory/bins/:id (getBin). |
| `lines[].binLocations[].binLocationCode` | string | no | The code of the bin the stock is taken from. |
| `lines[].binLocations[].quantity` | decimal | yes | The quantity taken from the bin, in the item's stocking unit. |
| `lines[].binLocations[].batchNumber` | string | no | The batch number of the batch this bin quantity is for. |
| `lines[].binLocations[].serialNumber` | string | no | The serial number of the unit this bin quantity is for. |
| `applications` | array<object> | no | The receipt lines that the returned quantity was taken from, for purchase return lines that return goods from a purchase order line. A purchase order line can be received on several receipts, so shipping records which receipt lines the quantity came from. Absent until the purchase return is shipped, and when no purchase return line returns goods from a purchase order line. |
| `applications[].receiptLineId` | string | yes | The unique identifier of the receipt line the quantity was taken from. Format: UUID. See `lines[].id` on GET inventory/receipts/:id (getReceipt). |
| `applications[].quantity` | decimal | yes | The quantity taken from the receipt line, in the purchase return line's unit of measure. |
| `applications[].unitCost` | decimal | no | The cost of one unit of the quantity taken from the receipt line, in the purchase return line's unit of measure, as taken off the inventory's value (relieved) when the purchase return was shipped. It is `totalCost` divided by `quantity`, not rounded. |
| `applications[].totalCost` | decimal | no | The cost of the whole quantity taken from the receipt line, as taken off the inventory's value (relieved) when the purchase return was shipped. |

```json
{
  "data": {
    "id": "6a1f30c9-4d82-4b57-9e60-83c25f0a71b4",
    "documentNumber": "PRTR-1043",
    "documentStatus": "DRAFT",
    "documentDate": "2026-08-20",
    "deliveryDate": "2026-01-15",
    "returnType": "NORMAL",
    "entityId": "1a9c4e70-38b5-4d26-9f01-7c2e5b8a6d43",
    "entityName": "<entityName>",
    "businessPartnerId": "b62e4f17-0c93-4a58-8d21-5f70e3b9a4c6",
    "businessPartnerName": "<businessPartnerName>",
    "referenceNumber": "<referenceNumber>",
    "originalReceiptId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "currency": "EUR",
    "totalAmount": "19.99",
    "notes": "<notes>",
    "createdDate": "2026-01-15T09:30:00.000Z",
    "lastModifiedDate": "2026-01-15T09:30:00.000Z",
    "lines": [
      {
        "id": "8c14a6f2-3b70-4d59-9e02-6f5a1d38b47c",
        "itemId": "5f21c8b0-9d34-4a76-8e12-04b93c7de5a8",
        "quantity": "30.00",
        "unitPrice": "3.20"
      }
    ],
    "applications": [
      {
        "receiptLineId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
        "quantity": "19.99",
        "unitCost": "19.99",
        "totalCost": "19.99"
      }
    ]
  },
  "diagnostics": [
    {
      "code": "<code>",
      "message": "<message>",
      "severity": "error",
      "path": [
        "<path>"
      ],
      "details": "<details>"
    }
  ]
}
```

## Response headers

- `Idempotency-Replayed` — Present and `true` only when this response replays a previously stored one for the same Idempotency-Key, rather than reflecting a live execution. Absent on a live response - never sent as `false`.

## Errors

| Code | Status |
| --- | --- |
| `REFERENCE_NOT_FOUND` | 400 |
| `NO_LINES` | 400 |
| `INVALID_QUANTITY` | 400 |
| `INCOMPLETE_LINE` | 400 |
| `MISSING_TRACKING_SELECTION` | 400 |
| `UNKNOWN_TRACKING_REFERENCE` | 400 |
| `POSTING_REJECTED` | 400 |
| `IDEMPOTENCY_KEY_CONFLICT` | 409 |
| `IDEMPOTENCY_PAYLOAD_MISMATCH` | 422 |

## Statuses

- `400` — Invalid request or business failure. Codes: REFERENCE_NOT_FOUND, NO_LINES, INVALID_QUANTITY, INCOMPLETE_LINE, MISSING_TRACKING_SELECTION, UNKNOWN_TRACKING_REFERENCE, POSTING_REJECTED
- `401` — The request carries no valid access token.
- `403` — Not authorized
- `404` — Not found
- `409` — The Idempotency-Key is still being processed by another request.
- `422` — The Idempotency-Key was already used with a different request payload.
- `429` — Too many requests from this client address. The response has no body and no `Retry-After` header.
- `500` — Internal server error
