Set Shipping Method
POST https://api.firmly.work/api/v2/domains/{domain}/cart/shipment/methods
Select a shipping method on a specific shipment. The cart returned in the response carries updated shipping cost, tax, and totals. After Set Shipping Info, each shipment is initialized with the merchant’s default method; call this endpoint when the buyer wants a different one — and, for SCHEDULED_DELIVERY shipments, to record the selected date/time slot.
Authentication
x-firmly-authorization(string, required) — Device access token from Browser Session
Path Parameters
domain(string, required) — The merchant’s domain (e.g.staging.luma.gift).
Request Body
-
shipment_id(string, required) — Target shipment identifier (from the cart’sshipments[].shipment_id). -
shipping_method_id(string, required) — ID of the method to select. Must match an entry in the shipment’sshipping_method_options[*].id. -
notes(string) — Optional delivery instructions for this shipment. -
selected_date(string) — ForSCHEDULED_DELIVERYshipments — the chosen date inYYYY-MM-DDformat. -
selected_time_slot(string) — ForSCHEDULED_DELIVERYshipments — the chosen time-slot identifier (theslot_idfrom Get Availability). Send theslot_idstring here; the response echoes the full slot object back on the shipment’sselected_time_slot.
Response
Returns the full cart — same schema as Get Cart. The target shipment’s shipping_method is set (plus notes, and selected_date / selected_time_slot for SCHEDULED_DELIVERY); cart totals are recalculated.
-
shipments(object[]) — The cart’s shipments. Each shipment has the shape below.Shipment
shipment_id(string, required) — Unique shipment identifier.platform_shipment_id(string) — Merchant-side shipment identifier, when available.line_item_ids(string[], required) — IDs of the line items grouped into this shipment.fulfillment_type(object, required) — The selected fulfillment type:{ id, name, description }, whereidis one ofSHIP_TO_ADDRESS,SCHEDULED_DELIVERY,PICKUP_IN_STORE.fulfillment_type_options(object[]) — Fulfillment types available for this shipment (same shape asfulfillment_type).shipping_method(object) — The selected shipping method.
shipping_method
id(string, required) — Method identifier.description(string, required) — Method description.price(object) — Cost (Amount). Required unlesshide_priceistrue.hide_price(boolean) — Whentrue, suppress the price in the UI.message(string) — Optional merchant note about this method.estimated_delivery(string) — Delivery estimate.
shipping_method_options(object[]) — Shipping methods available for this shipment (same shape asshipping_method).selected_location(object) — Pickup location — present forPICKUP_IN_STORE.{ location_id, name, address, phone?, operating_hours?, distance?, pickup_options }.selected_date(string) — Selected delivery date forSCHEDULED_DELIVERY(YYYY-MM-DD).selected_time_slot(object) — Selected time slot forSCHEDULED_DELIVERY:{ slot_id?, start_time, end_time, description?, price? }.pickup_option(object) — Selected pickup option:{ id, description, price? }.notes(string) — Delivery notes persisted on this shipment.
-
shipping_total(object) — Total shipping cost across all shipments (Amount). -
tax_total(object) — Total tax (Amount). -
total(object) — Grand total (Amount).
Examples
curl -X POST https://api.firmly.work/api/v2/domains/staging.luma.gift/cart/shipment/methods \-H "x-firmly-authorization: YOUR_TOKEN" \-H "Content-Type: application/json" \-d '{"shipment_id": "8568658c-bee9-9666-4547-a545fe27391b","shipping_method_id": "express-2day","notes": "Please leave at front door if no answer"}'
curl -X POST https://api.firmly.work/api/v2/domains/staging.luma.gift/cart/shipment/methods \-H "x-firmly-authorization: YOUR_TOKEN" \-H "Content-Type: application/json" \-d '{"shipment_id": "840b0477-b913-191a-2b4f-dc232a779f0c","shipping_method_id": "white-glove-setup","selected_date": "2026-06-15","selected_time_slot": "morning-premium","notes": "Please call 30 minutes before delivery"}'
Response Example
{"cart_id": "a5600193-2c13-3356-d063-711da63b7cd7","platform_id": "example_commerce","shop_id": "staging.luma.gift","cart_status": "active","line_items": [{"line_item_id": "c62279b9-3a72-0bbd-1377-be0033ad23dd","sku": "WS12-XS-Orange","description": "Radiant Tee","quantity": 2,"price": {"currency": "USD","value": 22.0,"number": 2200,"symbol": "$"},"line_price": {"currency": "USD","value": 44.0,"number": 4400,"symbol": "$"},"msrp": {"currency": "USD","value": 26.4,"number": 2640,"symbol": "$"},"image": {"url": "https://staging.luma.gift/radiant-tee.jpg","alt": "Radiant Tee","type": "default"}}],"shipments": [{"shipment_id": "8568658c-bee9-9666-4547-a545fe27391b","line_item_ids": ["c62279b9-3a72-0bbd-1377-be0033ad23dd"],"fulfillment_type": {"id": "SHIP_TO_ADDRESS","name": "Ship to Address","description": "Standard shipping to your address"},"shipping_method_options": [{"id": "standard-ground","description": "Standard Ground Shipping","price": {"currency": "USD","value": 9.99,"number": 999,"symbol": "$"},"estimated_delivery": "5-7 business days"},{"id": "express-2day","description": "Express 2-Day Shipping","price": {"currency": "USD","value": 19.99,"number": 1999,"symbol": "$"},"estimated_delivery": "2 business days"}],"shipping_method": {"id": "express-2day","description": "Express 2-Day Shipping","price": {"currency": "USD","value": 19.99,"number": 1999,"symbol": "$"},"estimated_delivery": "2 business days"},"notes": "Please leave at front door if no answer"}],"sub_total": {"currency": "USD","value": 44.0,"number": 4400,"symbol": "$"},"shipping_total": {"currency": "USD","value": 19.99,"number": 1999,"symbol": "$"},"tax_total": {"currency": "USD","value": 4.4,"number": 440,"symbol": "$"},"fees": [{"description": "Recycle fee","currency": "USD","value": 2.00,"number": 200,"symbol": "$"}],"fee_total": {"currency": "USD","value": 2.00,"number": 200,"symbol": "$"},"total": {"currency": "USD","value": 70.39,"number": 7039,"symbol": "$"},"schema_version": "2.0"}
Error Responses
Errors return a JSON body with code, error, and description. Program against the error value — descriptions are human-readable and may change.
400 — MissingAuthHeader
The x-firmly-authorization header is missing or empty.
{ "code": 400, "error": "MissingAuthHeader", "description": "x-firmly-authorization header is missing or invalid." }
400 — InvalidToken
The authorization token is not a valid JWT structure.
{ "code": 400, "error": "InvalidToken", "description": "Jwt token is invalid." }
400 — InvalidAPIToken
The API token (server-to-server secret) is invalid or has been revoked.
{ "code": 400, "error": "InvalidAPIToken", "description": "API token is invalid." }
400 — BadRequest
Server-to-server auth was attempted but required headers are missing or malformed.
{ "code": 400, "error": "BadRequest", "description": "Bad request." }
400 — InvalidShippingMethod
The supplied shipping_method_id is not in the shipment’s shipping_method_options.
{ "code": 400, "error": "InvalidShippingMethod", "description": "Invalid shipping method." }
400 — InvalidDeliveryDate
The supplied selected_date and/or selected_time_slot is not available for this shipment.
{ "code": 400, "error": "InvalidDeliveryDate", "description": "Invalid delivery date." }
401 — Unauthorized
The authorization token was rejected.
{ "code": 401, "error": "Unauthorized", "description": "Unauthorized." }
401 — InvalidJWTToken
The device JWT signature does not verify, or required claims are missing.
{ "code": 401, "error": "InvalidJWTToken", "description": "Jwt token is invalid." }
404 — PartnerNotFound
The appid claim on the device JWT does not map to a known partner / tenant.
{ "code": 404, "error": "PartnerNotFound", "description": "Partner not found." }
404 — DomainNotFound
The {domain} path parameter does not match any merchant configured with Firmly, or the merchant has been disabled.
{ "code": 404, "error": "DomainNotFound", "description": "This domain was not found in firmly servers." }
404 — CartNotFound
No cart exists for this device on this domain.
{ "code": 404, "error": "CartNotFound", "description": "Cart was not found." }
404 — ShipmentNotFound
The supplied shipment_id does not match any shipment on the current cart. Refresh cart state and retry with a current id.
{ "code": 404, "error": "ShipmentNotFound", "description": "Shipment not found." }
409 — MultipleDiscountCodesNotSupported
Setting the shipping method triggered re-evaluation of cart promotions and the merchant rejected the combined discount codes currently applied.
{ "code": 409, "error": "MultipleDiscountCodesNotSupported", "description": "Multiple discount codes are not supported." }
409 — NotEnoughStockError
One or more line items no longer have enough stock to fulfill the selected method. Lower the quantity or remove the item, then retry.
{ "code": 409, "error": "NotEnoughStockError", "description": "The amount of the required item is not available in stock." }
412 — MissingShippingInfo
Shipping address is not yet set on the cart. Call Set Shipping Info first.
{ "code": 412, "error": "MissingShippingInfo", "description": "Shipping info is missing." }
412 — OperationNotSupported
The merchant’s platform does not support this operation.
{ "code": 412, "error": "OperationNotSupported", "description": "This operation is not supported for this store." }
400 — InvalidInputBody
Request body fails schema validation. shipment_id and shipping_method_id must be non-empty strings.
{ "code": 400, "error": "InvalidInputBody", "description": "The body is not processable" }
422 — UnprocessableEntity
The request is well-formed but cannot be processed — typically the merchant API rejected the method change for a reason not covered by the more specific errors above.
{ "code": 422, "error": "UnprocessableEntity", "description": "The given payload has unprocessable, invalid or non-existent data. Please check the data and try again." }
429 — RateLimited
Too many requests in a short window. Back off and retry, honoring the Retry-After header.
{ "code": 429, "error": "RateLimited", "description": "Too many requests. Please try again later." }
503 — StoreUnavailable
The merchant’s API returned an unexpected response and the request could not be fulfilled. Retry with backoff.
{ "code": 503, "error": "StoreUnavailable", "description": "Store is unavailable." }