Skip to main content

registerShipmentStatus

Register a new per-shipment status. Must be called during bootstrap.

Import

import { registerShipmentStatus } from '@evershop/evershop/oms/services';

Syntax

registerShipmentStatus(
id: string,
detail: ShipmentStatus,
psoMapping?: Record<string, string>
): void

Parameters

id — Unique status identifier, non-empty and without spaces. Example: 'in_transit'. Registering an id that already exists throws; use addProcessor('shipmentStatus', ...) to mutate an existing entry instead.

detailShipmentStatus:

FieldTypeRequiredDescription
namestringYesDisplay name shown in the admin panel.
badgestringYesBadge variant — default, success, warning, destructive, outline.
phase'shipped' | 'delivered' | 'canceled'YesThe lifecycle bucket this status belongs to. Drives transition validation, the timestamp columns and the order-level rollup math.
phase is required

registerShipmentStatus throws Shipment status "<id>" must declare phase: 'shipped' | 'delivered' | 'canceled'. when phase is missing or is not one of those three values. There is no fourth phase and there is no pending phase — a shipment row only exists because something actually shipped.

isDefault and isCancelable were removed

They are no longer part of ShipmentStatus. The starting status is decided by createShipment (always shipped), not by a per-status flag. Cancelability is now driven by the oms.order.shipmentRollupCancelable map, keyed on the order-level rollup value, and is overridable with addProcessor('shipmentRollupCancelable', ...).

psoMapping (optional) — Extra {paymentStatus}:{orderShipmentRollup}orderStatus entries merged into oms.order.psoMapping.

The second segment is the rollup, not this status

psoMapping keys match order.shipment_status, which holds an order-level rollup value (pending, partially_shipped, shipped, partially_delivered, delivered, partially_canceled, canceled) — never a per-shipment status code. A mapping written against your custom status id will never match. See registerPSOStatusMapping.

Examples

Register a custom in-transit status

import { registerShipmentStatus } from '@evershop/evershop/oms/services';

export default () => {
registerShipmentStatus('in_transit', {
name: 'In Transit',
badge: 'default',
phase: 'shipped'
});
};

Because in_transit sits in the shipped phase, a shipment can move shipped → in_transit (same phase), and later in_transit → delivered or in_transit → canceled.

Register a terminal status

import { registerShipmentStatus } from '@evershop/evershop/oms/services';

export default () => {
registerShipmentStatus('returned_to_sender', {
name: 'Returned to Sender',
badge: 'destructive',
phase: 'canceled'
});
};

Canonical statuses

Core registers only shipped, delivered and canceled. For carrier integrations, CANONICAL_SHIPMENT_STATUSES provides a shared vocabulary aligned with the AfterShip / Shippo / EasyPost status sets, so multiple carrier extensions converge on the same codes instead of inventing their own:

import {
CANONICAL_SHIPMENT_STATUSES,
getShipmentStatusList,
registerShipmentStatus
} from '@evershop/evershop/oms/services';

export default () => {
for (const [code, detail] of Object.entries(CANONICAL_SHIPMENT_STATUSES)) {
if (!getShipmentStatusList()[code]) {
registerShipmentStatus(code, detail);
}
}
};

The constant covers in_transit, out_for_delivery, attempt_fail, available_for_pickup and exception (phase shipped), plus returned and expired (phase canceled). Core does not auto-register them — that is the extension's call. The getShipmentStatusList() guard matters: registering a code twice throws.

See Also