Set Billing Info
POST https://api.firmly.work/api/v2/domains/{domain}/cart/billing-info
Overview
Stages a billing address on the cart before the place-order call.
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 for billing -
last_name(string, required) — Buyer’s last name for billing -
address1(string, required) — Primary billing address line -
address2(string) — Secondary billing address line (apartment, suite, etc.) -
city(string, required) — Billing city name -
state_or_province(string, required) — Billing state or province code (e.g., “CA”, “NY”) or full name -
postal_code(string, required) — Billing ZIP or postal code -
country(string, required) — Billing country code in ISO 3166-1 alpha-2 format (e.g., “US”, “CA”) -
phone(string, required) — Billing contact phone number -
email(string, required) — Billing contact email address
Response
Returns a complete ShoppingCart object with updated billing information. The cart totals remain unchanged as billing address doesn’t affect shipping or tax calculations.
When to Use
Call this endpoint only when the merchant’s checkout flow requires billing info on the cart prior to place-order. This is not the default checkout pattern — for most merchants, billing info travels with the place-order request.
If you’re unsure whether your merchant needs it: when the merchant requires billing on the cart, calling place-order without this step fails with an OperationNotSupported-class error; otherwise you can skip this endpoint and pass billing_info in the place-order body.
Code Examples
curl -X POST https://api.firmly.work/api/v2/domains/staging.luma.gift/cart/billing-info \-H "x-firmly-authorization: YOUR_TOKEN" \-H "Content-Type: application/json" \-d '{"first_name": "Jane","last_name": "Doe","address1": "456 Billing Street","address2": "Suite 100","city": "Billing City","state_or_province": "NY","postal_code": "10001","country": "US","phone": "555-987-6543","email": "jane.doe@staging.luma.gift"}'
const billingInfo = {first_name: "Jane",last_name: "Doe",address1: "456 Billing Street",address2: "Suite 100",city: "Billing City",state_or_province: "NY",postal_code: "10001",country: "US",phone: "555-987-6543",email: "jane.doe@staging.luma.gift"};const response = await fetch('https://api.firmly.work/api/v2/domains/staging.luma.gift/cart/billing-info', {method: 'POST',headers: {'x-firmly-authorization': 'YOUR_TOKEN','Content-Type': 'application/json'},body: JSON.stringify(billingInfo)});const updatedCart = await response.json();
import requestsbilling_info = {"first_name": "Jane","last_name": "Doe","address1": "456 Billing Street","address2": "Suite 100","city": "Billing City","state_or_province": "NY","postal_code": "10001","country": "US","phone": "555-987-6543","email": "jane.doe@staging.luma.gift"}response = requests.post('https://api.firmly.work/api/v2/domains/staging.luma.gift/cart/billing-info',headers={'x-firmly-authorization': 'YOUR_TOKEN','Content-Type': 'application/json'},json=billing_info)updated_cart = response.json()
$billingInfo = ['first_name' => 'Jane','last_name' => 'Doe','address1' => '456 Billing Street','address2' => 'Suite 100','city' => 'Billing City','state_or_province' => 'NY','postal_code' => '10001','country' => 'US','phone' => '555-987-6543','email' => 'jane.doe@staging.luma.gift'];$ch = curl_init();curl_setopt($ch, CURLOPT_URL, 'https://api.firmly.work/api/v2/domains/staging.luma.gift/cart/billing-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($billingInfo));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"},"billing_info": {"first_name": "Jane","last_name": "Doe","address1": "456 Billing Street","address2": "Suite 100","city": "Billing City","state_or_province": "NY","postal_code": "10001","country": "US","phone": "555-987-6543","email": "jane.doe@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"}
Checkout Flow Integration
The typical checkout flow does not include this endpoint:
- Add Items to Cart
- Set Shipping Info
- Set Consents — when applicable
- Place Order —
billing_infois passed in this request body
Insert this endpoint between steps 2 and 4 only if your merchant integration requires billing info to be staged on the cart in advance.
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 — InvalidState
The state_or_province value is not valid for the given country.
{ "code": 400, "error": "InvalidState", "description": "Invalid state." }
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." }
412 — CountryNotSupported
The merchant does not accept billing addresses in the supplied country.
{ "code": 412, "error": "CountryNotSupported", "description": "Country is not supported." }
412 — OperationNotSupported
The merchant’s platform does not support staging billing info on the cart. For these merchants, billing info must be passed in the place-order request body instead.
{ "code": 412, "error": "OperationNotSupported", "description": "This operation is not supported for this store." }
400 — InvalidInputBody
Request body fails schema validation. Ensure all required fields are present and well-formed.
{ "code": 400, "error": "InvalidInputBody", "description": "The body is not processable" }