createShipment
Create a shipment for an order. A shipment covers a subset of the order's items — an order can have many shipments, and the order-level shipment_status is a rollup recomputed from all of them.
Import
import { createShipment } from '@evershop/evershop/oms/services';
createShipment is a named export of @evershop/evershop/oms/services.
Syntax
createShipment(
orderUuid: string,
payload: CreateShipmentPayload,
conn?: PoolClient
): Promise<CreateShipmentResult>
The legacy positional form createShipment(orderUuid, carrier, trackingNumber, connection?) throws. It created a shipment with no items, which is incompatible with the current shipment_item schema (qty > 0 is enforced). Pass the payload object instead.
Parameters
orderUuid
Type: string
The UUID of the order (the order.uuid column, not order_id).
payload
Type: CreateShipmentPayload
| Field | Type | Required | Description |
|---|---|---|---|
items | Array<{ order_item_id: number; qty: number }> | Yes | The order items (and quantities) included in this shipment. Must be non-empty; every qty must be a positive integer that does not exceed the item's unshipped remainder. |
carrier | string | Yes | The carrier code. Must be a registered carrier — see the note below. |
tracking_number | string | No | A tracking number you already have. When omitted, createShipment asks the carrier to create a label (if the carrier implements createLabel). |
notifyCustomer | boolean | No | Defaults to true. Forwarded on the shipment_created event payload; set false to suppress the shipment notification email. |
conn
Type: PoolClient (optional)
An existing connection with an open transaction. When provided, createShipment joins your transaction instead of opening (and committing) its own.
Return Value
Returns Promise<CreateShipmentResult>:
| Field | Type | Description |
|---|---|---|
shipment | ShipmentRow | The inserted shipment row. New shipments always land with status: 'shipped'. |
items | Array<{ order_item_id: number; qty: number }> | The items that were actually written to shipment_item. |
labelCreated | boolean | true when a label was purchased from the carrier during this call. |
Examples
Ship selected items with a known tracking number
import { createShipment } from '@evershop/evershop/oms/services';
const { shipment, items, labelCreated } = await createShipment(
'7afebbbd-69f6-4e2c-84c5-5b899173b867',
{
items: [
{ order_item_id: 41, qty: 2 },
{ order_item_id: 42, qty: 1 }
],
carrier: 'custom',
tracking_number: '1234567890'
}
);
console.log(shipment.uuid, items.length, labelCreated); // labelCreated === false
Let the carrier create the label
Omit tracking_number. If the registered carrier implements createLabel, the label is purchased inside createShipment (before the transaction opens) and the returned tracking number, label URL and carrier metadata are written onto the shipment row.
import { createShipment } from '@evershop/evershop/oms/services';
const result = await createShipment(orderUuid, {
items: [{ order_item_id: 41, qty: 1 }],
carrier: 'ups',
notifyCustomer: true
});
if (result.labelCreated) {
console.log(result.shipment.label_url, result.shipment.tracking_number);
}
Within your own transaction
import { createShipment } from '@evershop/evershop/oms/services';
import { getConnection } from '@evershop/evershop/lib/postgres';
import {
startTransaction,
commit,
rollback
} from '@evershop/evershop/lib/postgres/query';
const connection = await getConnection();
await startTransaction(connection);
try {
await createShipment(
orderUuid,
{
items: [{ order_item_id: 41, qty: 1 }],
carrier: 'custom',
tracking_number: '1Z999'
},
connection
);
await commit(connection);
} catch (e) {
await rollback(connection);
throw e;
}
The carrier must be registered
createShipment validates payload.carrier against the carrier registry and throws
Unknown carrier '<code>'. Install or register the carrier extension first. when it is not found. Register carriers from a module's bootstrap.ts:
import { registerCarrier } from '@evershop/evershop/oms/services';
export default () => {
registerCarrier({
code: 'my_carrier',
name: 'My Carrier',
description: 'Label + tracking integration'
});
};
Core ships one built-in carrier, custom ("Custom / Other"), with no capabilities — no createLabel, no tracking URL. Creating a shipment with it simply records that a shipment exists.
Label creation
Label purchase happens inside createShipment, not in a separate call:
tracking_numberprovided → no carrier call at all.tracking_numberomitted and the carrier implementscreateLabel→ the label is purchased outside the transaction (a network call must not hold a DB transaction open), then the resulting tracking number, label URL, label format, carrier shipment id and metadata are persisted with the shipment.tracking_numberomitted and the carrier has nocreateLabel→ the shipment is created withtracking_number = null. This is the normal path for the built-incustomcarrier.
If the transaction fails after a label was purchased, createShipment makes a best-effort compensating voidLabel() call so no orphan tracking number is left at the carrier.
Events
| Event | When | Payload |
|---|---|---|
shipment_created | Always, after commit | { shipmentId, orderId, notifyCustomer } |
shipment_label_created | Only when a label was purchased | { shipmentId, orderId, labelUrl, trackingNumber } |
Notes
- Validation runs twice: once before the transaction, and again under a per-order advisory lock, so two concurrent calls cannot over-allocate the same item.
- Items flagged
no_shipping_required(digital products) are rejected. - Quantities already covered by non-canceled shipments count against the remainder — shipping more than the unshipped quantity throws.
- After insert, the order's
shipment_statusrollup is recomputed, which in turn re-resolvesorder.statusthrough thepsoMapping. - An order activity log entry is written for every shipment.
- Hookable at
createShipment,validateShipmentItems,insertShipmentandinsertShipmentItems(hookBeforeCreateShipment/hookAfterCreateShipment, etc.).
See Also
- updateShipmentStatus - Advance a single shipment's status
- registerShipmentStatus - Register a custom per-shipment status
- cancelOrder - Cancel an order