Cart integration guide

Learn how to pass measurement data to Shopify cart line items so that the Measura app can correctly process and display measurement-based products.

Overview

This guide explains how to pass measurement data to Shopify cart line items so that the Measura app can correctly process and display measurement-based products. This is essential for mobile app builders, custom checkout integrations, and any third-party systems that need to add Measura products to a Shopify cart.

Using Shopify themes?

If your integration runs in Shopify theme code, use the public Storefront API on window.Measura to build these properties for you.

When to use this guide

Use this guide when:

  • Building a mobile app that integrates with Measura products
  • Creating a custom checkout experience
  • Integrating third-party systems with Measura
  • Programmatically adding measurement-based products to cart

Property types: hidden vs customer-facing

Cart line item properties fall into two categories:

Hidden properties (internal)

Properties starting with _ are hidden from customers and used internally by the Measura app for processing. These include:

  • _measura_data - Canonical measurement payload (required, see MeasuraData)

Related:

_measura_data is the canonical hidden field for measurement processing.

Customer-facing properties

Properties using localizedLabels are visible to customers in the cart. These labels are fetched from the Get app settings endpoint as part of the data.settings.localizedLabels field.

The LocalizedLabels object (returned by the app settings endpoint) contains keys for:

  • unitPrice - Label for unit price property name
  • unitPrice_{measurementType}_value_{unit} - Label for unit price property value
  • netWeight, netLength, netArea, netVolume, netTime - Labels for net measurement
  • netWeight_value_{unit}, etc. - Labels for net measurement values

Tip

Always fetch localizedLabels from the Get app settings endpoint to ensure proper localization and formatting. Replace tokens in the label values with actual data when adding to cart.

Required hidden properties (internal)

When adding a measurement-based product to the cart, you must include the following hidden properties (starting with _) on the line item. These are used internally by the Measura app and are not visible to customers:

  • _measura_data - Canonical measurement payload (see MeasuraData)

1. _measura_data (required)

Type: String (JSON)

Description: Canonical measurement payload used by Measura processing

Minimum Shape: measurement.type and measurement.dimensions[0] with unit and value

Full Schema: MeasuraData object

Rules:

  • Must be valid JSON
  • measurement.type must be one of: weight, length, area, volume, time
  • measurement.dimensions must include at least one entry
  • measurement.dimensions[0].value must be a positive number (> 0)
  • measurement.dimensions[0].unit must match a valid unit label from the API (see Getting valid unit labels from the API)
{
  "name": "_measura_data",
  "value": "{\"measurement\":{\"type\":\"weight\",\"dimensions\":[{\"unit\":\"kg\",\"value\":2.5}]},\"unitMeasurement\":{\"unit\":\"kg\",\"value\":1},\"properties\":{\"Net weight\":\"2.5 kg\"}}"
}

Optional hidden properties (internal)

No additional hidden Measura properties are required beyond _measura_data.

Customer-facing cart properties

To display measurement information to customers in the cart, add properties using the localized labels from the API. These labels are fetched from the Get app settings endpoint as part of the data.settings.localizedLabels field. These properties are visible to customers and should be formatted using the labels from localizedLabels.

Fetching localized labels from app settings

LocalizedLabels are returned by the Get app settings endpoint. Fetch them using:

GET /api/v1/app-settings?locale=en
Authorization: Bearer {apiKey}

The app settings response includes data.settings.localizedLabels with all available labels for the specified locale. If no locale is provided, the default locale (en) is used.

Related:

Important: Always fetch localizedLabels from the Get app settings endpoint. Do not hardcode label values, as they may vary by locale and configuration.

{
  "success": true,
  "data": {
    "settings": {
      "localizedLabels": {
        "unitPrice": "Price per",
        "unitPrice_weight_value_kg": "{Price} per {Quantity}kg",
        "netWeight": "Net weight",
        "netWeight_value_kg": "{Quantity} kg"
      }
    }
  }
}

Using localized labels in cart properties

After fetching localized labels, use them to create customer-visible cart properties. Replace tokens in the label values with actual data:

  • {Price} - Replace with formatted price (e.g., "$10.00")
  • {Quantity} - Replace with quantity value without space (e.g., "5kg"). When quantity is 1, omit this token.
  • {Quantity_With_Space} - Replace with quantity value with space (e.g., "5 kg"). When quantity is 1, omit this token.
  • {Unit} - Replace with unit label (e.g., "kg", "m", "L")
// Example: Using localizedLabels to create customer-facing properties
// First, fetch localizedLabels from the Get app settings endpoint:
// GET /api/v1/app-settings?locale=en
// Response includes: data.settings.localizedLabels
//
// Example localizedLabels from app settings:
// {
//   "unitPrice": "Price per",
//   "unitPrice_weight_value_kg": "{Price} per {Quantity}kg",
//   "netWeight": "Net weight",
//   "netWeight_value_kg": "{Quantity} kg"
// }

// Cart properties (visible to customers):
{
  "name": "Price per",  // from localizedLabels.unitPrice
  "value": "$10.00 per 5kg"  // from localizedLabels.unitPrice_weight_value_kg
    // Replaced: {Price} → "$10.00", {Quantity} → "5"
},
{
  "name": "Net weight",  // from localizedLabels.netWeight
  "value": "5 kg"  // from localizedLabels.netWeight_value_kg
    // Replaced: {Quantity} → "5"
}

Available localized label keys

The LocalizedLabels object (returned by the Get app settings endpoint) contains dynamic keys based on enabled measurement types and units. Common patterns include:

  • unitPrice - Unit price property name label
  • unitPrice_{measurementType}_value_{unit} - Unit price value label (e.g., unitPrice_weight_value_kg)
  • netWeight, netLength, netArea, netVolume, netTime - Net measurement labels
  • netWeight_value_{unit}, etc. - Net measurement value labels (e.g., netWeight_value_kg)

Important

Always fetch localizedLabels from the Get app settings endpoint and use the exact label values as the property names. The property values should be the label templates with tokens replaced with actual data.

Complete examples

Example 1: Adding a weight-based product

This example includes both hidden properties (for Measura processing) and customer-facing properties (using localizedLabels):

{
  "id": "gid://shopify/ProductVariant/123456789",
  "quantity": 5,
  "properties": [
    // Hidden properties (internal, not visible to customers)
    {
      "name": "_measura_data",
      "value": "{\"measurement\":{\"type\":\"weight\",\"dimensions\":[{\"unit\":\"kg\",\"value\":2.5}]},\"unitMeasurement\":{\"unit\":\"kg\",\"value\":1}}"
    },
    // Customer-facing properties (visible to customers)
    {
      "name": "Price per",  // from localizedLabels.unitPrice
      "value": "$10.00 per 5kg"  // from localizedLabels.unitPrice_weight_value_kg
    },
    {
      "name": "Net weight",  // from localizedLabels.netWeight
      "value": "5 kg"  // from localizedLabels.netWeight_value_kg
    }
  ]
}

Example 2: Adding a length-based product

{
  "id": "gid://shopify/ProductVariant/987654321",
  "quantity": 1,
  "properties": [
    // Hidden properties (internal)
    {
      "name": "_measura_data",
      "value": "{\"measurement\":{\"type\":\"length\",\"dimensions\":[{\"unit\":\"m\",\"value\":10}]},\"unitMeasurement\":{\"unit\":\"m\",\"value\":1}}"
    },
    // Customer-facing properties
    {
      "name": "Net length",  // from localizedLabels.netLength
      "value": "10 m"  // from localizedLabels.netLength_value_m
    }
  ]
}

Example 3: Adding a volume-based product with container weight

{
  "id": "gid://shopify/ProductVariant/456789123",
  "quantity": 1,
  "properties": [
    // Hidden properties (internal)
    {
      "name": "_measura_data",
      "value": "{\"measurement\":{\"type\":\"volume\",\"dimensions\":[{\"unit\":\"L\",\"value\":3.5}]},\"unitMeasurement\":{\"unit\":\"L\",\"value\":1},\"properties\":{\"Container weight\":\"0.1 kg\"}}"
    },
    // Customer-facing properties
    {
      "name": "Net volume",  // from localizedLabels.netVolume
      "value": "3.5 L"  // from localizedLabels.netVolume_value_L
    }
  ]
}

Getting valid unit labels from the API

The most reliable way to get the correct unit label is from the API response. When you fetch a product or variant using the Measura API, the unitMeasurement.unit field contains the exact unit label you should use in _measura_data.measurement.dimensions[0].unit.

Example API response:

{
  "success": true,
  "data": {
    "variant": {
      "id": "123456789",
      "unitMeasurement": {
        "unit": "kg",
        "value": 1.0
      }
    }
  }
}

In this case, use "kg" as the unit label in your cart properties.

Tip

Always use the unit label from the API response rather than hardcoding values. This ensures compatibility with any unit configuration changes.

For a complete reference of all supported units, see the unit enum documentation: WeightUnit, LengthUnit, AreaUnit, VolumeUnit, and TimeUnit.

Common pitfalls and troubleshooting

Issue: Measurement data not being recognized

Symptoms: Cart line items are not processed as measurement-based products

Solutions:

  1. Verify _measura_data exists and is valid JSON
  2. Verify measurement.type is one of: weight, length, area, volume, time
  3. Verify measurement.dimensions[0] includes both unit and value
  4. Verify the unit label exactly matches the API response (unitMeasurement.unit)
  5. Verify the numeric value is positive (> 0)

Issue: Invalid unit error

Symptoms: Unit label is not recognized

Solutions:

  1. Check that the unit label matches exactly (case-sensitive)
  2. Verify the unit is valid for the measurement type (e.g., don't use kg for length type)
  3. Get the unit label from the API response rather than hardcoding

Integration workflow

Step 1: Fetch product/variant data and app settings

Use the Measura API to get product information and localized labels:

// Fetch variant data
GET /api/v1/products/{productId}/variants/{variantId}
Authorization: Bearer {apiKey}

// Fetch app settings to get localizedLabels
// localizedLabels are returned in data.settings.localizedLabels
GET /api/v1/app-settings?locale=en
Authorization: Bearer {apiKey}

See get specific variant and get app settings endpoint documentation.

Related:

Remember: localizedLabels are fetched from the Get app settings endpoint, not from the variant endpoint.

Step 2: Extract measurement data and localized labels

From the API responses, extract:

  • measurementType (from product or variant response)
  • unitMeasurement.unit (the unit label to use, from variant response)
  • unitMeasurement.value (the base unit value, from variant response)
  • localizedLabels (from app settings response: data.settings.localizedLabels, for customer-facing properties)

Step 3: Build the canonical _measura_data payload

Build a JSON payload with at least measurement.type and measurement.dimensions[0] (unit + value), then serialize it into _measura_data.

Step 4: Build cart line item properties

Construct the properties array with:

  • Hidden properties: include _measura_data only
  • Customer-facing properties: Use localizedLabels (from the Get app settings endpoint) to create visible properties, replacing tokens with actual values

Step 5: Add to cart

Add the line item to the Shopify cart with the properties array.

Shopify cart API integration

Using Shopify Storefront API

This example shows how to use localizedLabels fetched from the Get app settings endpoint:

// localizedLabels should be fetched from GET /api/v1/app-settings
// Response: data.settings.localizedLabels
const addToCart = async (cartId, variantId, measurementType, value, unit, localizedLabels, price, quantity) => {
  const mutation = `
    mutation cartLinesAdd($cartId: ID!, $lines: [CartLineInput!]!) {
      cartLinesAdd(cartId: $cartId, lines: $lines) {
        cart {
          id
        }
      }
    }
  `;

  // Build canonical measura payload
  const measuraData = {
    measurement: {
      type: measurementType,
      dimensions: [{ unit, value }]
    },
    unitMeasurement: {
      unit,
      value: 1
    }
  };

  // Build hidden properties
  const hiddenProperties = [
    { key: "_measura_data", value: JSON.stringify(measuraData) }
  ];

  // Build customer-facing properties from localizedLabels
  const customerProperties = [];
  if (localizedLabels.unitPrice && localizedLabels[`unitPrice_${measurementType}_value_${unit}`]) {
    const unitPriceLabel = localizedLabels.unitPrice;
    const unitPriceTemplate = localizedLabels[`unitPrice_${measurementType}_value_${unit}`];
    const unitPriceValue = unitPriceTemplate
      .replace("{Price}", price)
      .replace("{Quantity}", quantity === 1 ? "" : `${quantity}${unit}`)
      .replace("{Quantity_With_Space}", quantity === 1 ? "" : `${quantity} ${unit}`);
    customerProperties.push({ key: unitPriceLabel, value: unitPriceValue });
  }

  const variables = {
    cartId: cartId,
    lines: [{
      merchandiseId: variantId,
      quantity: quantity,
      attributes: [...hiddenProperties, ...customerProperties]
    }]
  };

  // Execute mutation...
};

Using Shopify AJAX API

This example shows how to use localizedLabels fetched from the Get app settings endpoint:

// First, fetch localizedLabels from GET /api/v1/app-settings
// Response includes: data.settings.localizedLabels
// Example: Adding to cart with hidden and customer-facing properties
const localizedLabels = {
  unitPrice: "Price per",
  unitPrice_weight_value_kg: "{Price} per {Quantity}kg",
  netWeight: "Net weight",
  netWeight_value_kg: "{Quantity} kg"
};

const quantity = 5;
const price = "$10.00";
const unit = "kg";
const measurementValue = 2.5;

const measuraData = {
  measurement: {
    type: "weight",
    dimensions: [{ unit, value: measurementValue }]
  },
  unitMeasurement: {
    unit,
    value: 1
  }
};

// Build properties
const properties = {
  // Hidden properties (internal)
  '_measura_data': JSON.stringify(measuraData),
  // Customer-facing properties
  [localizedLabels.unitPrice]: localizedLabels.unitPrice_weight_value_kg
    .replace("{Price}", price)
    .replace("{Quantity}", quantity === 1 ? "" : `${quantity}${unit}`),
  [localizedLabels.netWeight]: localizedLabels.netWeight_value_kg
    .replace("{Quantity}", quantity)
    .replace("{Unit}", unit)
};

fetch('/cart/add.js', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    id: variantId,
    quantity: quantity,
    properties: properties
  })
});

Testing your integration

  1. Test with API data: Use real product/variant data from the Measura API
  2. Verify properties: Check that all required properties are present
  3. Validate format: Ensure _measura_data is valid JSON with required measurement fields
  4. Test different types: Test with all measurement types (weight, length, area, volume, time)
  5. Test edge cases: Test with decimal values, different units, and boundary values