Update a warehouse transfer
Operation ID: updateWarehouseTransfer
Group: Inventory › Warehouse Transfers
Open in the interactive reference
PATCH /inventory/warehouse-transfers/{id}
Retry-safe: A write that accepts Idempotency-Key: repeating a request with the same key replays the stored answer instead of executing twice.
Updates a draft warehouse transfer with the fields you send. A field you leave out keeps its current value. transferType can't be changed.
Lines change through three separate arrays, all applied in one transaction, in this order: updateLines, createLines, deleteLines. Leave out all three to keep the lines as they are. One request can remove a line and add its replacement.
updateLines and deleteLines take the line ids returned by GET inventory/warehouse-transfers/:id, and a line id from another transfer is refused. createLines also needs locationId and targetLocationId in the same request.
On an updateLines item, a field you leave out keeps its current value. Sending batches, serials, or binLocations replaces all of that line's tracking, so send every batch, serial, and bin location the line should keep. Leave out batches, serials, and binLocations to keep the line's tracking as it is.
The response has the same fields as GET inventory/warehouse-transfers/:id.
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. |
id |
path | string | yes | The unique identifier of the transfer you want to update. |
Request body
Required, application/json: updateWarehouseTransferInput.
| Field | Type | Required | Description |
|---|---|---|---|
locationId |
string | no | The warehouse the goods leave, applied to every line. See id on GET inventory/warehouses/:id. |
targetLocationId |
string | no | The warehouse the goods go to. It must differ from locationId. See id on GET inventory/warehouses/:id. |
documentDate |
date | no | The date to give the transfer. Use format YYYY-MM-DD. |
referenceNumber |
string | no | Your own reference on the transfer. |
notes |
string | no | Free text on the document. Example: "Consolidated with WTR-2290". |
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. |
createLines |
array<object> | no | Lines to add. Same shape as on create. |
createLines[].itemId |
string | yes | The item to move. See id on GET inventory/items/:id. |
createLines[].unitId |
string | no | The unit the quantity is in. Required on a new line. See id on GET inventory/unit-of-measures/:id. |
createLines[].quantity |
decimal | yes | How much to move, in the line's unit. Must be greater than zero. |
createLines[].notes |
string | no | Free text on the line. |
createLines[].unitTransferValue |
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. |
createLines[].batches |
array<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. |
createLines[].batches[].batchNumberId |
string | no | The unique identifier for the batch. See id on GET inventory/batch-numbers. Either this or batchNumber is required. |
createLines[].batches[].batchNumber |
string | no | The batch number as printed. Either this or batchNumberId is required; alone, it must name exactly one batch of the item in the warehouse the goods leave. Given beside batchNumberId, it must be that batch's own number. |
createLines[].batches[].quantity |
decimal | yes | How much of the line this batch accounts for. |
createLines[].serials |
array<object> | no | The serialized units that move. Required for a serialized item, one entry per unit. |
createLines[].serials[].serialNumberId |
string | no | The unique identifier for the unit. See id on GET inventory/serial-numbers. Either this or serialNumber is required. |
createLines[].serials[].serialNumber |
string | no | The serial number stamped on the unit. Either this or serialNumberId is required; alone, it must name exactly one unit of the item in the warehouse the goods leave. Given beside serialNumberId, it must be that unit's own number. |
createLines[].binLocations |
array<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. |
createLines[].binLocations[].fromBinLocationId |
string | no | The bin the goods leave, in the source warehouse. See id on GET inventory/bins/:id. |
createLines[].binLocations[].toBinLocationId |
string | no | The bin the goods arrive in, in the target warehouse. See id on GET inventory/bins/:id. |
createLines[].binLocations[].quantity |
decimal | yes | How much moves between these two bins. |
createLines[].binLocations[].batchNumber |
string | no | The batch this entry belongs to, when it belongs to one of the line's batches. |
createLines[].binLocations[].serialNumber |
string | no | The serialized unit this entry belongs to, when it belongs to one of the line's serials. |
updateLines |
array<object> | no | Lines to change, each addressed by its id. Every field but the id is optional, and a tracking array you send replaces what the line had. |
updateLines[].id |
string | yes | The unique identifier for the line to change. |
updateLines[].itemId |
string | no | The item to move. See id on GET inventory/items/:id. |
updateLines[].unitId |
string | no | The unit the quantity is in. Leave it out to keep the line's current unit. See id on GET inventory/unit-of-measures/:id. |
updateLines[].quantity |
decimal | no | How much to move, in the line's unit. Must be greater than zero. |
updateLines[].notes |
string | no | Free text on the line. |
updateLines[].unitTransferValue |
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. |
updateLines[].batches |
array<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. |
updateLines[].batches[].batchNumberId |
string | no | The unique identifier for the batch. See id on GET inventory/batch-numbers. Either this or batchNumber is required. |
updateLines[].batches[].batchNumber |
string | no | The batch number as printed. Either this or batchNumberId is required; alone, it must name exactly one batch of the item in the warehouse the goods leave. Given beside batchNumberId, it must be that batch's own number. |
updateLines[].batches[].quantity |
decimal | yes | How much of the line this batch accounts for. |
updateLines[].serials |
array<object> | no | The serialized units that move. Required for a serialized item, one entry per unit. |
updateLines[].serials[].serialNumberId |
string | no | The unique identifier for the unit. See id on GET inventory/serial-numbers. Either this or serialNumber is required. |
updateLines[].serials[].serialNumber |
string | no | The serial number stamped on the unit. Either this or serialNumberId is required; alone, it must name exactly one unit of the item in the warehouse the goods leave. Given beside serialNumberId, it must be that unit's own number. |
updateLines[].binLocations |
array<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. |
updateLines[].binLocations[].fromBinLocationId |
string | no | The bin the goods leave, in the source warehouse. See id on GET inventory/bins/:id. |
updateLines[].binLocations[].toBinLocationId |
string | no | The bin the goods arrive in, in the target warehouse. See id on GET inventory/bins/:id. |
updateLines[].binLocations[].quantity |
decimal | yes | How much moves between these two bins. |
updateLines[].binLocations[].batchNumber |
string | no | The batch this entry belongs to, when it belongs to one of the line's batches. |
updateLines[].binLocations[].serialNumber |
string | no | The serialized unit this entry belongs to, when it belongs to one of the line's serials. |
deleteLines |
array<string> | no | The ids of lines to remove. They are applied before the updates and the creates. |
{
"locationId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"targetLocationId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"documentDate": "2026-01-15",
"referenceNumber": "<referenceNumber>",
"notes": "Consolidated with WTR-2290",
"contractualCurrency": "<contractualCurrency>",
"createLines": [
{
"itemId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"unitId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"quantity": "19.99",
"notes": "<notes>",
"unitTransferValue": "19.99",
"batches": [
{
"batchNumberId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"batchNumber": "<batchNumber>",
"quantity": "19.99"
}
],
"serials": [
{
"serialNumberId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"serialNumber": "<serialNumber>"
}
],
"binLocations": [
{
"fromBinLocationId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"toBinLocationId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"quantity": "19.99",
"batchNumber": "<batchNumber>",
"serialNumber": "<serialNumber>"
}
]
}
],
"updateLines": [
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"itemId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"unitId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"quantity": "19.99",
"notes": "<notes>",
"unitTransferValue": "19.99",
"batches": [
{
"batchNumberId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"batchNumber": "<batchNumber>",
"quantity": "19.99"
}
],
"serials": [
{
"serialNumberId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"serialNumber": "<serialNumber>"
}
],
"binLocations": [
{
"fromBinLocationId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"toBinLocationId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"quantity": "19.99",
"batchNumber": "<batchNumber>",
"serialNumber": "<serialNumber>"
}
]
}
],
"deleteLines": [
"<deleteLines>"
]
}
Response
200 Success
Under data (updateWarehouseTransferOutput):
| Field | Type | Required | Description |
|---|---|---|---|
id |
string | yes | The unique identifier for the transfer. Example: "c41f9a08-6b23-4e57-90d8-2f7a5c31e0b6". |
documentNumber |
string | no | The transfer's number, for example 'WTR-2291'. Example: "WTR-2291". |
documentStatus |
string | yes | The status of the transfer. Possible values: 'DRAFT', 'COMMITTED', 'CANCELLED'. Example: "DRAFT". |
documentDate |
date | no | The date the transfer is dated. Format YYYY-MM-DD. |
referenceNumber |
string | no | Your own reference on the transfer. |
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. |
entityId |
string | no | The legal entity the goods leave. See id on GET base/entities. |
entityName |
string | no | The name of that legal entity. |
toEntityId |
string | no | The legal entity receiving the goods, on an intercompany transfer. See id on GET base/entities. |
toEntityName |
string | no | The name of that legal entity. |
locationId |
string | no | The warehouse the goods leave. See id on GET inventory/warehouses/:id. |
locationCode |
string | no | The code of that warehouse. |
locationName |
string | no | The name of that warehouse. |
targetLocationId |
string | no | The warehouse the goods go to. See id on GET inventory/warehouses/:id. |
targetLocationCode |
string | no | The code of that warehouse. |
targetLocationName |
string | no | The name of that warehouse. |
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. |
intercompanyBillId |
string | no | The numeric id of the intercompany bill this transfer created, not a UUID. It does not match id on GET accounting/intercompany-bills/:id. |
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. |
currency |
string | no | The currency sourceAmount is stated in, the source entity's functional currency. Format ISO 4217, for example 'EUR'. |
sourceAmount |
decimal | no | What the goods were worth leaving the source entity, in currency. Written by the commit. |
destinationCurrency |
string | no | The currency destinationAmount is stated in, the destination entity's functional currency. Format ISO 4217, for example 'GBP'. Differs from currency on an intercompany transfer between entities with different functional currencies. |
destinationAmount |
decimal | no | What the goods were worth arriving at the destination entity, in destinationCurrency. Written by the commit. On an intercompany transfer it differs from sourceAmount; when both share a currency, the difference is the margin. |
notes |
string | no | Free text on the document. Example: "Consolidated with WTR-2290". |
lineCount |
number | no | The number of lines on the transfer. Example: 1. |
createdDate |
date-time | no | The date and time the transfer was created, in UTC. Format YYYY-MM-DDTHH:mm:ss.sssZ. |
lastModifiedDate |
date-time | no | The date and time the transfer record last changed, in UTC. Format YYYY-MM-DDTHH:mm:ss.sssZ. Example: "2026-08-18T11:20:04.219Z". |
lines |
array<object> | no | The lines of the transfer. The collection GET inventory/warehouse-transfers returns lineCount in place of these. |
lines[].id |
string | yes | The unique identifier for the transfer line. updateLines and deleteLines address lines by it. |
lines[].itemId |
string | no | The item being moved. See id on GET inventory/items/:id. |
lines[].itemCode |
string | no | The code of that item. |
lines[].itemName |
string | no | The name of that item. |
lines[].locationId |
string | no | The warehouse this line draws from, the document's warehouse applied to every line. See id on GET inventory/warehouses/:id. |
lines[].locationCode |
string | no | The code of that warehouse. |
lines[].locationName |
string | no | The name of that warehouse. |
lines[].targetLocationId |
string | no | The warehouse this line delivers to. See id on GET inventory/warehouses/:id. |
lines[].targetLocationCode |
string | no | The code of that warehouse. |
lines[].targetLocationName |
string | no | The name of that warehouse. |
lines[].unitId |
string | no | The unit the quantity is in. See id on GET inventory/unit-of-measures/:id. |
lines[].unitCode |
string | no | The code of that unit. |
lines[].quantity |
decimal | no | How much moves, in the line's unit. |
lines[].inventoryQuantity |
decimal | no | The same amount in the item's stocking unit, which is what the warehouse counts. |
lines[].transferCost |
decimal | no | What one unit left the source warehouse at, in the transfer's currency. The valuation method sets it at commit. Absent on a draft. |
lines[].unitTransferValue |
decimal | no | The contractual price of one unit on an intercompany transfer, in the contractual currency. Supplied on create. |
lines[].totalTransferValue |
decimal | no | The line's total on an intercompany transfer, in the contractual currency: totalTransferValue = unitTransferValue x quantity. Written by the platform. |
lines[].returnedQuantity |
decimal | no | How much of this line has come back on an intercompany return. |
lines[].status |
string | no | The status of the line. |
lines[].notes |
string | no | Free text on the line. |
lines[].batches |
array<object> | no | The batches that make up the line's quantity. |
lines[].batches[].batchNumberId |
string | no | The unique identifier for the batch number. See id on GET inventory/batch-numbers/:id. |
lines[].batches[].batchNumber |
string | no | The batch number. |
lines[].batches[].quantity |
decimal | no | How much of the line this batch accounts for. |
lines[].batches[].inventoryQuantity |
decimal | no | The same amount in the item's stocking unit. |
lines[].batches[].unitCost |
decimal | no | What this batch moved at, in the transfer's currency. Set at commit. |
lines[].serials |
array<object> | no | The serialized units that move. |
lines[].serials[].serialNumberId |
string | no | The unique identifier for the serial number. See id on GET inventory/serial-numbers/:id. |
lines[].serials[].serialNumber |
string | no | The serial number. |
lines[].serials[].quantity |
decimal | no | Always one, for a serialized unit. |
lines[].serials[].unitCost |
decimal | no | What this unit moved at, in the transfer's currency. Set at commit. |
lines[].binLocations |
array<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. |
lines[].binLocations[].fromBinLocationId |
string | no | The bin the goods leave, in the source warehouse. See id on GET inventory/bins/:id. |
lines[].binLocations[].fromBinLocationCode |
string | no | The code of that bin. |
lines[].binLocations[].toBinLocationId |
string | no | The bin the goods arrive in, in the target warehouse. See id on GET inventory/bins/:id. |
lines[].binLocations[].toBinLocationCode |
string | no | The code of that bin. |
lines[].binLocations[].quantity |
decimal | no | How much moves between these two bins. |
lines[].binLocations[].inventoryQuantity |
decimal | no | The same amount in the item's stocking unit. |
lines[].binLocations[].batchNumber |
string | no | The batch this entry belongs to, when it belongs to one of the line's batches. |
lines[].binLocations[].serialNumber |
string | no | The serialized unit this entry belongs to, when it belongs to one of the line's serials. |
{
"data": {
"id": "c41f9a08-6b23-4e57-90d8-2f7a5c31e0b6",
"documentNumber": "WTR-2291",
"documentStatus": "DRAFT",
"documentDate": "2026-01-15",
"referenceNumber": "<referenceNumber>",
"transferType": "<transferType>",
"entityId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"entityName": "<entityName>",
"toEntityId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"toEntityName": "<toEntityName>",
"locationId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"locationCode": "<locationCode>",
"locationName": "<locationName>",
"targetLocationId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"targetLocationCode": "<targetLocationCode>",
"targetLocationName": "<targetLocationName>",
"billingStatus": "<billingStatus>",
"intercompanyBillId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"contractualCurrency": "<contractualCurrency>",
"currency": "<currency>",
"sourceAmount": "19.99",
"destinationCurrency": "<destinationCurrency>",
"destinationAmount": "19.99",
"notes": "Consolidated with WTR-2290",
"lineCount": 1,
"createdDate": "2026-01-15T09:30:00.000Z",
"lastModifiedDate": "2026-08-18T11:20:04.219Z",
"lines": [
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"itemId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"itemCode": "<itemCode>",
"itemName": "<itemName>",
"locationId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"locationCode": "<locationCode>",
"locationName": "<locationName>",
"targetLocationId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"targetLocationCode": "<targetLocationCode>",
"targetLocationName": "<targetLocationName>",
"unitId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"unitCode": "<unitCode>",
"quantity": "19.99",
"inventoryQuantity": "19.99",
"transferCost": "19.99",
"unitTransferValue": "19.99",
"totalTransferValue": "19.99",
"returnedQuantity": "19.99",
"status": "<status>",
"notes": "<notes>",
"batches": [
{
"batchNumberId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"batchNumber": "<batchNumber>",
"quantity": "19.99",
"inventoryQuantity": "19.99",
"unitCost": "19.99"
}
],
"serials": [
{
"serialNumberId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"serialNumber": "<serialNumber>",
"quantity": "19.99",
"unitCost": "19.99"
}
],
"binLocations": [
{
"fromBinLocationId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"fromBinLocationCode": "<fromBinLocationCode>",
"toBinLocationId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"toBinLocationCode": "<toBinLocationCode>",
"quantity": "19.99",
"inventoryQuantity": "19.99",
"batchNumber": "<batchNumber>",
"serialNumber": "<serialNumber>"
}
]
}
]
},
"diagnostics": [
{
"code": "<code>",
"message": "<message>",
"severity": "error",
"path": [
"<path>"
],
"details": "<details>"
}
]
}
Response headers
Idempotency-Replayed— Present andtrueonly 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 asfalse.
Errors
| Code | Status |
|---|---|
NOT_FOUND |
400 |
WRONG_STATUS |
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: NOT_FOUND, WRONG_STATUS, REFERENCE_NOT_FOUND, NO_LINES, INVALID_QUANTITY, SAME_LOCATION, INTERCOMPANY_CONFIG_MISSING, DUPLICATE_TRACKING_NUMBER, UNKNOWN_TRACKING_REFERENCE, POSTING_REJECTED401— The request carries no valid access token.403— Not authorized404— Not found409— 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 noRetry-Afterheader.500— Internal server error