# Creates several warehouse transfers in one call.

`POST /inventory/warehouse-transfers/bulk`

Group: Inventory › Warehouse Transfers · Operation ID: `bulkCreateWarehouseTransfers`

Creates several warehouse transfers in one call. It is all or nothing: one rejected transfer rejects the whole request and nothing is written, so a failed import is safe to retry unchanged. 'index' on an error names the transfer at fault, counting from zero. The response has the created transfers in input order, headers only, and each can be read in full on GET inventory/warehouse-transfers/:id.

[Open in the interactive reference](https://docs.api.lensing.ai/?op=bulkCreateWarehouseTransfers)

## Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `Api-Version` | header | `"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`: bulkCreateWarehouseTransfersInput.

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `warehouseTransfers` | array of object | yes | The transfers to create. All or nothing: if any is rejected, none is created. |
| `warehouseTransfers[].locationId` | string | yes | The warehouse the goods leave, applied to every line. See id on GET inventory/warehouses/:id. |
| `warehouseTransfers[].targetLocationId` | string | yes | The warehouse the goods go to. It must differ from locationId. See id on GET inventory/warehouses/:id. |
| `warehouseTransfers[].entityId` | string | no | The legal entity. Defaults to the entity that owns the source warehouse. See id on GET base/entities. |
| `warehouseTransfers[].documentDate` | string (date) | no | The date to give the transfer. Defaults to today. Use format YYYY-MM-DD. |
| `warehouseTransfers[].referenceNumber` | string | no | Your own reference on the transfer. |
| `warehouseTransfers[].notes` | string | no | Free text on the document. |
| `warehouseTransfers[].transferType` | string | no | The type of transfer to create. Accepted values: 'NORMAL', 'INTERCOMPANY'. Defaults to 'NORMAL'. 'INTERCOMPANY_RETURN' is created by the intercompany return flow, not here. |
| `warehouseTransfers[].toEntityId` | string | no | The legal entity receiving the goods. Required when transferType is 'INTERCOMPANY'. See id on GET base/entities. |
| `warehouseTransfers[].contractualCurrency` | string | no | The currency an intercompany transfer is contracted in. Required when transferType is 'INTERCOMPANY'. Format ISO 4217, for example 'EUR'. Each line's unitTransferValue is stated in it. |
| `warehouseTransfers[].lines` | array of object | yes | The lines of the transfer. At least one is required. |
| `warehouseTransfers[].lines[].itemId` | string | yes | The item to move. See id on GET inventory/items/:id. |
| `warehouseTransfers[].lines[].unitId` | string | no | The unit the quantity is in. Defaults to the item's stocking unit. There is no endpoint for it. |
| `warehouseTransfers[].lines[].quantity` | string (decimal) | yes | How much to move, in the line's unit. Must be greater than zero. |
| `warehouseTransfers[].lines[].notes` | string | no | Free text on the line. |
| `warehouseTransfers[].lines[].unitTransferValue` | string (decimal) | no | The contractual price of one unit, in the transfer's contractual currency. Required on an intercompany transfer. The two entities agree the price, and the API cannot derive it from a cost. |
| `warehouseTransfers[].lines[].batches` | array of object | no | The batches that make up the line's quantity. Required for a batch-tracked item, and they must add up to the line's quantity. |
| `warehouseTransfers[].lines[].batches[].batchNumberId` | string | no | The unique identifier for an existing batch number. See id on GET inventory/batch-numbers/:id. |
| `warehouseTransfers[].lines[].batches[].batchNumber` | string | no | The batch number. Naming one the item does not have yet creates it. |
| `warehouseTransfers[].lines[].batches[].quantity` | string (decimal) | yes | How much of the line this batch accounts for. |
| `warehouseTransfers[].lines[].serials` | array of object | no | The serialized units that move. Required for a serialized item, one entry per unit. |
| `warehouseTransfers[].lines[].serials[].serialNumberId` | string | no | The unique identifier for an existing serial number. See id on GET inventory/serial-numbers/:id. |
| `warehouseTransfers[].lines[].serials[].serialNumber` | string | no | The serial number. Naming one the item does not have yet creates it. |
| `warehouseTransfers[].lines[].binLocations` | array of object | no | The bins the goods leave and arrive in. Each entry names both ends of the move, and the batch or serial it belongs to where it belongs to one. Leave both names off for a quantity that moves at line level. |
| `warehouseTransfers[].lines[].binLocations[].fromBinLocationId` | string | no | The bin the goods leave, in the source warehouse. See id on GET inventory/bins/:id. |
| `warehouseTransfers[].lines[].binLocations[].toBinLocationId` | string | no | The bin the goods arrive in, in the target warehouse. See id on GET inventory/bins/:id. |
| `warehouseTransfers[].lines[].binLocations[].quantity` | string (decimal) | yes | How much moves between these two bins. |
| `warehouseTransfers[].lines[].binLocations[].batchNumber` | string | no | The batch this entry belongs to, when it belongs to one of the line's batches. |
| `warehouseTransfers[].lines[].binLocations[].serialNumber` | string | no | The serialized unit this entry belongs to, when it belongs to one of the line's serials. |

Example:

```json
{
  "warehouseTransfers": [
    {
      "locationId": "b6f0d2a4-77c1-4e39-8a52-0c4e1f9b73d5",
      "targetLocationId": "e94a17c3-5b28-4d60-91f7-3a8c2e05b6d1",
      "lines": [
        {
          "itemId": "517a93e4-cbb4-4571-9e3d-2343e56cfd84",
          "quantity": "24"
        }
      ]
    }
  ]
}
```

## Response

`200` Success

Under `data` (bulkCreateWarehouseTransfersOutput):

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `warehouseTransfers` | array of object | yes | The created transfers, in input order. Headers only. Read one in full on GET inventory/warehouse-transfers/:id. |
| `warehouseTransfers[].id` | string | yes | The unique identifier for the transfer. |
| `warehouseTransfers[].documentNumber` | string | no | The transfer's number, for example 'WTR-2291'. |
| `warehouseTransfers[].documentStatus` | string | yes | The status of the transfer. Possible values: 'DRAFT', 'COMMITTED', 'CANCELLED'. |
| `warehouseTransfers[].documentDate` | string (date) | no | The date the transfer is dated. Format YYYY-MM-DD. |
| `warehouseTransfers[].referenceNumber` | string | no | Your own reference on the transfer. |
| `warehouseTransfers[].transferType` | string | no | The type of transfer. Possible values: 'NORMAL', 'INTERCOMPANY', 'INTERCOMPANY_RETURN'. An intercompany transfer sells the goods to another legal entity and can be billed. |
| `warehouseTransfers[].entityId` | string | no | The legal entity the goods leave. See id on GET base/entities. |
| `warehouseTransfers[].entityName` | string | no | The name of that legal entity. |
| `warehouseTransfers[].toEntityId` | string | no | The legal entity receiving the goods, on an intercompany transfer. See id on GET base/entities. |
| `warehouseTransfers[].toEntityName` | string | no | The name of that legal entity. |
| `warehouseTransfers[].locationId` | string | no | The warehouse the goods leave. See id on GET inventory/warehouses/:id. |
| `warehouseTransfers[].locationCode` | string | no | The code of that warehouse. |
| `warehouseTransfers[].locationName` | string | no | The name of that warehouse. |
| `warehouseTransfers[].targetLocationId` | string | no | The warehouse the goods go to. See id on GET inventory/warehouses/:id. |
| `warehouseTransfers[].targetLocationCode` | string | no | The code of that warehouse. |
| `warehouseTransfers[].targetLocationName` | string | no | The name of that warehouse. |
| `warehouseTransfers[].billingStatus` | string | no | The billing status of the transfer. Possible values: 'NOT_BILLED', 'BILLED'. Only an intercompany transfer is billed, through POST inventory/warehouse-transfers/:id/bill. |
| `warehouseTransfers[].intercompanyBillId` | string | no | The identifier of the intercompany bill this transfer created, as the platform's own number. GET accounting/vendor-bills/:id takes a UUID, so this number cannot fetch that bill. |
| `warehouseTransfers[].contractualCurrency` | string | no | The currency an intercompany transfer is contracted in. Format ISO 4217, for example 'EUR'. Each line's unitTransferValue is stated in it. |
| `warehouseTransfers[].currency` | string | no | The currency the amounts on the transfer are stated in, the source entity's functional currency. Format ISO 4217, for example 'EUR'. |
| `warehouseTransfers[].sourceAmount` | string (decimal) | no | What the goods were worth leaving the source entity, in the transfer's currency. Written by the commit. |
| `warehouseTransfers[].destinationAmount` | string (decimal) | no | What the goods were worth arriving at the destination entity, in the transfer's currency. On an intercompany transfer it differs from sourceAmount, and the difference is the margin. |
| `warehouseTransfers[].notes` | string | no | Free text on the document. |
| `warehouseTransfers[].lineCount` | number | no | The number of lines on the transfer. |
| `warehouseTransfers[].createdDate` | string (date-time) | no | The date and time the transfer was created, in UTC. Format YYYY-MM-DDTHH:mm:ss.sssZ. |
| `warehouseTransfers[].lastModifiedDate` | string (date-time) | no | The date and time the transfer record last changed, in UTC. Format YYYY-MM-DDTHH:mm:ss.sssZ. |

## 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 |
| --- | --- |
| `NOTHING_TO_CREATE` | 400 |
| `REFERENCE_NOT_FOUND` | 400 |
| `NO_LINES` | 400 |
| `INVALID_QUANTITY` | 400 |
| `SAME_LOCATION` | 400 |
| `INTERCOMPANY_CONFIG_MISSING` | 400 |
| `DUPLICATE_TRACKING_NUMBER` | 400 |
| `UNKNOWN_TRACKING_REFERENCE` | 400 |
| `POSTING_REJECTED` | 400 |
| `IDEMPOTENCY_KEY_CONFLICT` | 409 |
| `IDEMPOTENCY_PAYLOAD_MISMATCH` | 422 |

## Statuses

- `400` — Invalid request or business failure. Codes: NOTHING_TO_CREATE, REFERENCE_NOT_FOUND, NO_LINES, INVALID_QUANTITY, SAME_LOCATION, INTERCOMPANY_CONFIG_MISSING, DUPLICATE_TRACKING_NUMBER, UNKNOWN_TRACKING_REFERENCE, POSTING_REJECTED
- `401` — Missing or invalid 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
- `500` — Internal server error
