View Categories

Pricing Queries

5 min read

A rental product has no single price — it has a set of tiers, and what a customer pays depends on the dates they picked, how long that is, how many they want, what group they are in, and whether their dates land on a special or surge period. Two queries expose that: rentalProductPrice quotes one specific set of dates, and rentalProductPriceList returns the configured tiers so you can render a pricing card. Neither needs a token.

Quoting a set of dates #

query Quote {
  rentalProductPrice(input: {
    sku: "CANON-R6"
    start_date: "2026-12-24 09:00:00"
    end_date: "2026-12-28 17:00:00"
    qty: 2
  }) {
    price
    row_total
    currency
    has_special_pricing
    duration { minutes hours days weeks months }
    breakdown {
      label
      period_type
      period_quantity
      start_date
      end_date
      price
      is_special
    }
  }
}
{
  "data": {
    "rentalProductPrice": {
      "price": 265.00,
      "row_total": 530.00,
      "currency": "GBP",
      "has_special_pricing": true,
      "duration": { "minutes": 5880, "hours": 98, "days": 4, "weeks": 0.5833, "months": 0.1361 },
      "breakdown": [
        {
          "label": "Christmas surge",
          "period_type": null,
          "period_quantity": null,
          "start_date": "2026-12-24 09:00:00",
          "end_date": "2026-12-26 23:59:59",
          "price": 150.00,
          "is_special": true
        },
        {
          "label": "2 days",
          "period_type": "day",
          "period_quantity": 2,
          "start_date": "2026-12-27 00:00:00",
          "end_date": "2026-12-28 17:00:00",
          "price": 115.00,
          "is_special": false
        }
      ]
    }
  }
}
  • price is the unit price for the whole period, excluding tax. row_total is price × qty.
  • breakdown shows how the engine carved the period up: one entry for each date-based pricing period that matched (labelled with that period’s name), plus one for each stretch left over that the standard tiers priced. The entries sum to price.
  • has_special_pricing is true only when a special or surge period actually contributed to the total. Where a special would have made the hire more expensive than the plain tiers, the engine keeps the cheaper plain price — and then this stays false and the breakdown shows the plain tiers, so it never claims credit for something that is not in the number.
  • currency is the store’s display currency.

The dates that come back may not be the dates you sent #

start_date and end_date in the response are the dates the pricing engine actually priced, which are not always the ones you asked about. A product that does not use times has its end pushed to the end of the day; a single-day product is collapsed to one day; products configured to add time to the calculation get their end moved accordingly. duration describes that same normalised span, so the two always agree with each other and with price. For ordinary inputs on an ordinary product they are identical to what you sent.

Reading duration #

minutes, hours and days are whole elapsed units rounded down; weeks and months are fractional, with a month counted as 30 days — the same arithmetic the tiers themselves use, so a duration of weeks: 1.0 is exactly the span a one-week tier covers.

Turning parts of the calculation off #

include_specials: false ignores date-based pricing periods and include_price_rules: false ignores catalog price rules. Both default to true. They are useful for showing a customer what they saved, or for a back-office screen that needs the list price:

rentalProductPrice(input: {
  sku: "CANON-R6"
  start_date: "2026-12-24 09:00:00"
  end_date: "2026-12-28 17:00:00"
  include_specials: false
}) { price }

The price list #

For a pricing card on a product page — the tiers themselves, with no dates involved.

query PricingCard {
  rentalProductPriceList(sku: "CANON-R6") {
    currency
    customer_group_id
    items {
      period_type
      period_quantity
      price
      additional_price
      additional_period_type
      qty_start
      qty_end
      is_special
      special_name
    }
  }
}
{
  "items": [
    { "period_type": "day",  "period_quantity": 1, "price": 65.00, "additional_price": 45.00,
      "additional_period_type": "day", "qty_start": 1, "qty_end": null,
      "is_special": false, "special_name": null },
    { "period_type": "week", "period_quantity": 1, "price": 260.00, "additional_price": null,
      "additional_period_type": null, "qty_start": 1, "qty_end": null,
      "is_special": false, "special_name": null },
    { "period_type": "day",  "period_quantity": 1, "price": 95.00, "additional_price": null,
      "additional_period_type": null, "qty_start": 1, "qty_end": null,
      "is_special": true,  "special_name": "Christmas surge" }
  ]
}
  • additional_price is what each period beyond the tier costs — the first row above reads “£65 for the first day, £45 for each further day”.
  • qty_start/qty_end bound the quantity a tier applies to, for volume pricing. A null qty_end means no upper bound.
  • is_special: true rows belong to a date-based pricing period named by special_name. They only apply on that period’s dates, so label them accordingly rather than showing them as if they were always available.
  • period_type is minute, hour, day, week, month or year.
  • Rows come back shortest period first, then by length, with base tiers ahead of special tiers of the same length — which reads top-to-bottom the way a pricing card should.
  • A product with no tiers configured returns an empty items list rather than an error. rentalProductPrice on such a product returns price: 0 with an empty breakdown.

Customer groups #

Both queries price for the calling customer’s group by default — send a customer token and trade pricing follows automatically, with no customer_group_id needed.

You can pass customer_group_id explicitly, but a customer may only pass their own; anything else is refused. Quoting an arbitrary group is a back-office capability and needs a token with back-office permission. Without that rule, any visitor could read your trade prices by guessing group ids.

Quote, then book #

These queries price dates; they do not hold them. A quote is a statement about the tiers, not a reservation, and nothing stops another customer taking the last unit a second later. Check availability alongside the price, and treat the booking as real only once addRentalProductsToCart has accepted it — that is the point at which the dates are re-checked and the line is priced for real.