Docs
Firmly Agentic Commerce
Set theme to dark (⇧+D)

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 requests
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"
}
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:

  1. Add Items to Cart
  2. Set Shipping Info
  3. Set Consents — when applicable
  4. Place Order — billing_info is 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" }