Back to Arky

Shipping

Get shipping rates and create shipments

The Shipping module quotes rates, purchases labels, and tracks order fulfillment. Arky selects the backend shipping route and returns carrier, service, rate, tracking, and label details.

Get Shipping Rates

POST /v1/stores/{storeId}/orders/{orderId}/shipping/rates
SDK: sdk.eshop.shipment.getRates()

Get available shipping rates for selected quantities in an order. Send the persisted fulfillment location, selected order-product quantities, and package facts. Arky loads the origin from the location, loads the destination from the order, and validates both saved addresses before contacting the provider. International quotes also require a complete customs declaration.

import { formatMinor } from "arky-sdk/utils";

const rates = await sdk.eshop.shipment.getRates({
	order_id: "ord_abc123",
	location_id: "loc_warehouse",
	lines: [
		{ order_product_id: "order_product_1", quantity: 2 },
		{ order_product_id: "order_product_2", quantity: 1 },
	],
	parcel: {
		length: 10,
		width: 8,
		height: 4,
		weight: 2,
		distance_unit: "in",
		mass_unit: "lb",
	},
});

rates.forEach((rate) => {
	console.log(
		`${rate.carrier} ${rate.service}: ${formatMinor(rate.amount, rate.currency)} (${rate.estimated_days} days)`,
	);
});

Parameters

Name Type Description
order_id required string Order ID to get rates for
location_id required string Persisted fulfillment location whose address is the origin
lines required ShippingRateLine[] Selected OrderProduct IDs and positive quantities
parcel required Parcel Package dimensions and weight
customs_declaration optional CustomsDeclaration Customs info for international shipments

ShippingRateLine Object

Parameters

Name Type Description
order_product_id required string OrderProduct included in this package
quantity required number Positive quantity included in this package

Parcel Object

Parameters

Name Type Description
length required number Package length
width required number Package width
height required number Package height
weight required number Package weight
distance_unit required string Distance unit (cm, in, ft, mm, m, yd)
mass_unit required string Weight unit (oz, lb, g, kg)

Rate Response

[
	{
		"id": "rate_abc",
		"carrier": "usps",
		"service": "priority",
		"display_name": "USPS Priority Mail",
		"amount": 895,
		"currency": "USD",
		"estimated_days": 3
	}
]
Note

The returned id is an opaque, expiring signed quote. It binds the order and location revisions, selected item quantities, and provider rate details. Use it with the same location and lines when creating the shipment. If the order or location changes, request rates again.

Create Shipment

POST /v1/stores/{storeId}/orders/{orderId}/shipments
SDK: sdk.eshop.shipment.create()

Purchase a shipping label and create a shipment for an order. Generate one shipment ID for the intended shipment and reuse it with the exact same request if the call is interrupted. The response contains both shipment_id and the complete shipment resource.

import {
	clearDurableRequest,
	getOrCreateDurableRequest,
	withDurableRequestLock,
} from "arky-sdk/utils";

const request = {
	order_id: "ord_abc123",
	rate_id: "rate_abc",
	location_id: "loc_warehouse",
	fulfillment_order_id: "fo_abc123",
	lines: [
		{
			order_product_id: "order_product_1",
			fulfillment_order_line_id: "fol_1",
			quantity: 2,
		},
		{
			order_product_id: "order_product_2",
			fulfillment_order_line_id: "fol_2",
			quantity: 1,
		},
	],
};
const storageKey = "arky:shipping-label:store_abc123:ord_abc123";

const result = await withDurableRequestLock(
	storageKey,
	"shipping-label purchase",
	async () => {
		const durable = getOrCreateDurableRequest(
			storageKey,
			request,
			"shipping-label purchase",
		);
		const response = await sdk.eshop.shipment.create({
			...request,
			shipment_id: durable.id,
		});
		if (
			response.shipment_id !== durable.id ||
			response.shipment.id !== durable.id
		) {
			throw new Error(
				"Shipping response did not confirm the requested shipment",
			);
		}
		clearDurableRequest(durable, "shipping-label purchase");
		return response;
	},
);

console.log(
	result.shipment.tracking_number,
	result.shipment.shippo_label?.label_url,
);

Parameters

Name Type Description
order_id required string Order ID
shipment_id required UUID string Stable caller-owned ID for this shipment resource
rate_id required string Rate ID from getRates response
location_id required string Fulfillment location ID
fulfillment_order_id optional string Fulfillment order being shipped. Recommended for new orders.
lines required ShipmentLine[] Items being shipped
Warning

Do not generate a new shipment ID in an automatic retry. Arky durably records the label purchase before contacting Shippo and never automatically repeats a purchase that may have reached the provider. Retrieve the shipment with sdk.eshop.shipment.get() and inspect shippo_label.status. A failed purchase can be retried explicitly with sdk.eshop.shipment.retry() only when its evidence proves the provider call did not start. unknown is terminal and can never be retried.

Note

Shipment status is one of pending, label_created, in_transit, out_for_delivery, delivered, failed, returned, or cancelled. The one Shippo label purchase and its one label-refund workflow are embedded on shippo_label; shipment money movements remain separate settlement records.

ShipmentLine Object

Parameters

Name Type Description
order_product_id required string OrderProduct ID
fulfillment_order_line_id required string Fulfillment order line being satisfied
quantity required number Quantity being shipped

Customs Declaration (International)

For international shipments, include a customs declaration when fetching rates:

const rates = await sdk.eshop.shipment.getRates({
	// ... other params
	customs_declaration: {
		contents_type: "MERCHANDISE",
		contents_explanation: "Clothing items",
		non_delivery_option: "RETURN",
		certify: true,
		certify_signer: "John Doe",
		items: [
			{
				description: "T-Shirt",
				quantity: 2,
				net_weight: "0.5",
				mass_unit: "lb",
				value_amount: "25.00",
				value_currency: "usd",
				origin_country: "US",
			},
		],
	},
});

For US-origin exports, set eel_pfc only from the shipment’s actual filing basis and include aes_itn when using AES_ITN. Optional incoterm support depends on the selected carrier; omit these fields unless they apply.

Note

The rate request never accepts address overrides. Correct the order destination or fulfillment-location address, then request rates again if validation fails.

Refund Shippo Label

POST /v1/stores/{storeId}/orders/{orderId}/shipments/{shipmentId}/shippo-label/refund
SDK: sdk.eshop.shipment.refund.request()

Request the single managed refund for a purchased Shippo label. The refund is an embedded child of that label because there can be at most one. Repeating the request returns the same durable child; it does not create another Shippo refund.

const refund = await sdk.eshop.shipment.refund.request({
	order_id: "ord_abc123",
	shipment_id: "shipment_abc123",
});

console.log(refund.id, refund.status, refund.safe_error);

Parameters

Name Type Description
order_id required string Order ID
shipment_id required string Shipment whose managed label should be refunded

The refund status is requested, processing, succeeded, rejected, failed, or unknown. Read it through shipment.shippo_label.refund. A failed refund can be retried explicitly with sdk.eshop.shipment.refund.retry() only when its evidence proves the Shippo call did not start; unknown is terminal and can never be retried.

Shipment Settlements

Every platform debit or compensating credit for a shipment is a separate OrderShipmentSettlement because a shipment can cause multiple money movements with independent lifecycles. List them with sdk.eshop.shipment.settlement.find(), fetch one with .get(), and explicitly retry a safely retryable failure with .retry(). Settlement types are purchase_debit, purchase_compensation_credit, and refund_credit; direction is debit or credit.