Set Shipping Info
POST https://api.firmly.work/api/v2/domains/{domain}/cart/shipping-info
Overview
Sets buyer shipping information for cart delivery. The endpoint validates the address, calculates shipping costs based on available shipping methods, and updates cart totals. It supports multi-shipment scenarios where different items may ship from different locations.
Authentication
x-firmly-authorization(string, required) — Device access token from Browser Session
Path Parameters
domain(string, required) — Domain of the merchant website (e.g.,staging.luma.gift)
Request Body
-
first_name(string, required) — Buyer’s first name -
last_name(string, required) — Buyer’s last name -
address1(string, required) — Primary address line (street address) -
address2(string) — Secondary address line (apartment, suite, unit, etc.) -
city(string, required) — City name -
state_or_province(string, required) — State or province code (e.g., “CA”, “NY”) or full name -
postal_code(string, required) — ZIP or postal code -
country(string, required) — Country code in ISO 3166-1 alpha-2 format (e.g., “US”, “CA”) -
phone(string, required) — Contact phone number for delivery -
email(string, required) — Contact email address
Response
Returns a complete ShoppingCart object with:
- Updated shipping information
- Calculated shipping costs per shipment
- Updated tax calculations based on shipping address
- Recalculated cart totals
Multi-Shipment Behavior
The shipping address applies to all shipments in the cart. Key behaviors:
- Address Application: The provided address is used for all shipments
- Shipping Calculation: Each shipment’s shipping cost is calculated independently
- Tax: The merchant’s platform recalculates tax for the shipping destination and returns it in the cart
- Validation: Address is validated against merchant’s supported regions
Code Examples
curl -X POST https://api.firmly.work/api/v2/domains/staging.luma.gift/cart/shipping-info \-H "x-firmly-authorization: YOUR_TOKEN" \-H "Content-Type: application/json" \-d '{"first_name": "John","last_name": "Smith","address1": "123 Main Street","address2": "Apt 4B","city": "Anytown","state_or_province": "CA","postal_code": "12345","country": "US","phone": "555-123-4567","email": "john.smith@staging.luma.gift"}'
const shippingInfo = {first_name: "John",last_name: "Smith",address1: "123 Main Street",address2: "Apt 4B",city: "Anytown",state_or_province: "CA",postal_code: "12345",country: "US",phone: "555-123-4567",email: "john.smith@staging.luma.gift"};const response = await fetch('https://api.firmly.work/api/v2/domains/staging.luma.gift/cart/shipping-info', {method: 'POST',headers: {'x-firmly-authorization': 'YOUR_TOKEN','Content-Type': 'application/json'},body: JSON.stringify(shippingInfo)});const updatedCart = await response.json();
import requestsshipping_info = {"first_name": "John","last_name": "Smith","address1": "123 Main Street","address2": "Apt 4B","city": "Anytown","state_or_province": "CA","postal_code": "12345","country": "US","phone": "555-123-4567","email": "john.smith@staging.luma.gift"}response = requests.post('https://api.firmly.work/api/v2/domains/staging.luma.gift/cart/shipping-info',headers={'x-firmly-authorization': 'YOUR_TOKEN','Content-Type': 'application/json'},json=shipping_info)updated_cart = response.json()
$shippingInfo = ['first_name' => 'John','last_name' => 'Smith','address1' => '123 Main Street','address2' => 'Apt 4B','city' => 'Anytown','state_or_province' => 'CA','postal_code' => '12345','country' => 'US','phone' => '555-123-4567','email' => 'john.smith@staging.luma.gift'];$ch = curl_init();curl_setopt($ch, CURLOPT_URL, 'https://api.firmly.work/api/v2/domains/staging.luma.gift/cart/shipping-info');curl_setopt($ch, CURLOPT_POST, true);curl_setopt($ch, CURLOPT_HTTPHEADER, ['x-firmly-authorization: YOUR_TOKEN','Content-Type: application/json']);curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($shippingInfo));curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);$response = curl_exec($ch);$updatedCart = json_decode($response, true);curl_close($ch);
Response Example
{"line_items": [{"line_item_id": "a02a3e67-b50e-8dcd-5970-8fff52117e43","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": ["a02a3e67-b50e-8dcd-5970-8fff52117e43"],"fulfillment_type": {"id": "SHIP_TO_ADDRESS","name": "Ship to Address","description": "Standard shipping to your address"},"shipping_method": {"id": "STANDARD_GROUND","description": "Standard Ground (5-7 business days)","price": {"currency": "USD","value": 9.99,"number": 999,"symbol": "$"},"estimated_delivery": "5-7 business days"}}],"shipping_info": {"first_name": "John","last_name": "Smith","address1": "123 Main Street","address2": "Apt 4B","city": "Anytown","state_or_province": "CA","postal_code": "12345","country": "US","phone": "555-123-4567","email": "john.smith@staging.luma.gift"},"sub_total": {"currency": "USD","value": 44.0,"number": 4400,"symbol": "$"},"shipping_total": {"currency": "USD","value": 9.99,"number": 999,"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": 60.39,"number": 6039,"symbol": "$"},"schema_version": "2.0"}
Related Operations
After setting shipping information, typical next steps include:
- Set Billing Info — Add billing address
- Set Consents — Update consent preferences
- Complete Order — Complete the purchase
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 — InvalidShippingInfo
Address fields fail merchant-level validation (e.g. PO Box where prohibited, line length exceeded, format rejected by the merchant).
{ "code": 400, "error": "InvalidShippingInfo", "description": "Invalid shipping info." }
400 — InvalidState
The state_or_province value is not valid for the given country (e.g. "ZZ" for country: "US").
{ "code": 400, "error": "InvalidState", "description": "Invalid state." }
400 — ErrorInvalidEmail
The email field is not a syntactically valid email address.
{ "code": 400, "error": "ErrorInvalidEmail", "description": "Invalid email." }
400 — ShippingNotNeeded
The cart contains only items that do not require shipping (e.g. digital-only). Skip this endpoint.
{ "code": 400, "error": "ShippingNotNeeded", "description": "Shipping is not needed." }
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. Add an item first via Add Line Item.
{ "code": 404, "error": "CartNotFound", "description": "Cart was not found." }
409 — ShipmentNotAvailable
No shipping method is available for the supplied address — usually because the merchant does not ship to that region or the cart contents are restricted there.
{ "code": 409, "error": "ShipmentNotAvailable", "description": "Shipment is not available." }
409 — ShippingAddressNotSupported
The supplied address is rejected by the merchant for reasons beyond country/state validation (e.g. APO/FPO, US territories, embargoed regions). Retry with a different address.
{ "code": 409, "error": "ShippingAddressNotSupported", "description": "Shipping address is not supported." }
409 — MultipleDiscountCodesNotSupported
Setting shipping triggered a re-evaluation of cart promotions and the merchant rejected the combination of currently-applied discount codes. Remove one or more promos and retry.
{ "code": 409, "error": "MultipleDiscountCodesNotSupported", "description": "Multiple discount codes are not supported." }
409 — NotEnoughStockError
One or more line items no longer have enough stock after the shipping update. 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 — CountryNotSupported
The merchant does not ship to the supplied country. Use one of the merchant’s supported countries.
{ "code": 412, "error": "CountryNotSupported", "description": "Country is not supported." }
412 — ProductNotSupported
One or more items in the cart cannot be shipped to the supplied address (e.g. age-restricted, hazardous, region-locked).
{ "code": 412, "error": "ProductNotSupported", "description": "Product is not supported." }
412 — NoLineItemError
The cart is empty after the merchant call — typically the merchant rejected the cart contents on shipping update. Refresh cart state.
{ "code": 412, "error": "NoLineItemError", "description": "No line item." }
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. Ensure all required fields (first_name, last_name, address1, city, state_or_province, postal_code, country, phone, email) are present and well-formed.
{ "code": 400, "error": "InvalidInputBody", "description": "The body is not processable" }
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." }