E097: Create transfer between locations
POST /api/materials-inventory/transfers
Records the outbound leg of a materials transfer between two locations. Triggered from the Materials Inventory Transfers page, "New transfer". The single outbound row encodes both source and destination; the inbound leg is added later via E098 when the receiving location confirms arrival.
Authentication
Standard tenant route. Requires Authorization: Bearer <firebase-id-token> and X-Org-Id: <org-id>. Access: Member. Also reachable with an org-scoped PAT.
Request
{
"materialId": "uuid",
"sourceLocationId": "uuid",
"destinationLocationId": "uuid",
"quantity": 100,
"eventDate": "2026-06-10T00:00:00Z",
"notes": "..."
}
quantity must be positive. eventDate and notes are optional. The ref is generated server-side as TFR-MI-<seq> (per-tenant); the endpoint writes a single TRANSFER row carrying to_location_id, and the matching RECEIVE row is written when E098 completes it.
Response — 201 Created
{
"ledgerEntry": {
"id": "uuid",
"orgId": "uuid",
"code": "MIL-001",
"materialId": "uuid",
"locationId": "uuid",
"eventType": "TRANSFER",
"quantity": "100",
"unitCost": null,
"ref": "TFR-MI-042",
"toLocationId": "uuid",
"notes": "...",
"eventDate": "2026-06-10T00:00:00Z",
"sourceType": null,
"sourceId": null,
"createdAt": "2026-06-10T00:00:00Z"
},
"ref": "TFR-MI-042"
}
locationId is the source; toLocationId is the destination. Numeric columns arrive as strings.
Errors
| HTTP | code | Condition |
|---|---|---|
| 404 | not_found | Material, source, or destination location not found. |
| 422 | validation_failed | Source and destination are the same, quantity is not positive, or the material is inactive. |