View Categories

Launching soon on the Shopify App StoreJoin the waitlist

GraphQL API reference: Rentals & Bookings

19 min read

The GraphQL API gives your systems one address for everything in Rentals & Bookings. You ask for exactly the fields you need, and several things can come back in one request. This page shows how to send a request and lists every query and mutation of Rentals & Bookings by area. The input and object types they use are on GraphQL API reference: Rentals & Bookings types. The add-on apps’ operations are on GraphQL API reference: add-on apps. Keys, versions, limits and errors: API overview.

Send a request #

AddressPOST https://YOUR-API-ADDRESS/graphql/2026-10 (Settings › API shows it)
HeadersAuthorization: Bearer sirb_your_secret_key and Content-Type: application/json
Body{"query": "…", "variables": {…}, "operationName": "…"} (variables and operationName are optional)
Answer{"data": {…}}, with errors when something couldn’t run

Queries may also be sent with GET ?query=…&variables=…; mutations only with POST. Send one operation per request. Every operation on this page needs the Rentals & Bookings app; queries need a secret key, and mutations a key with Allow changes (write). The API keys operations work with a key of any of the apps.

curl -s "https://YOUR-API-ADDRESS/graphql/2026-10" \
  -H "Authorization: Bearer sirb_your_secret_key" \
  -H "Content-Type: application/json" \
  -d '{"query": "{ rentalShop { domain currency timezone today } rentalApiCaller { scopes installedApps } }"}'
# 200
{"data": {"rentalShop": {"domain": "your-store.myshopify.com", "currency": "USD", "timezone": "America/New_York", "today": "2026-11-30"},
          "rentalApiCaller": {"scopes": ["read", "write"], "installedApps": ["core", "contracts", "deposits"]}}}

The same from JavaScript on your server (Node.js 18 or later, which has fetch):

const RENTALS = 'https://YOUR-API-ADDRESS/graphql/2026-10';
const KEY = process.env.RENTALS_API_KEY;          // sirb_…: keep it on the server

async function rentals(query, variables = {}) {
  const res = await fetch(RENTALS, {
    method: 'POST',
    headers: { 'Authorization': `Bearer ${KEY}`, 'Content-Type': 'application/json' },
    body: JSON.stringify({ query, variables }),
  });
  if (res.status === 429) throw new Error(`Rate limited, retry in ${res.headers.get('Retry-After')} s`);
  const body = await res.json();
  if (body.errors?.length) throw Object.assign(new Error(body.errors[0].message), body.errors[0].extensions);
  return body.data;
}

// Is a tent free the first weekend of December, and what does it cost?
const pick = { variantId: 'gid://shopify/ProductVariant/44001', startDate: '2026-12-04', endDate: '2026-12-06', quantity: 2 };
const data = await rentals(`
  query Check($a: RentalAvailabilityInput!, $p: RentalPriceInput!) {
    rentalProductAvailability(input: $a) { isAvailable availableQuantity }
    rentalProductPrice(input: $p) { total currency lines { label amount } }
  }`, { a: pick, p: pick });
console.log(data.rentalProductAvailability.isAvailable, data.rentalProductPrice.total);

To explore without code, open Settings › API › Open the GraphQL explorer, paste your key in the Headers pane as {"Authorization": "Bearer sirb_your_secret_key"}, and use its Docs pane to browse every type.

How the schema works #

  • Ids are global: gid://sirentals/Booking/42 for the app’s objects (Booking, Hold, SerialNumber, Movement, Kit, Balance, CalendarImport, ApiKey …) and Shopify’s own ids for Shopify’s objects (gid://shopify/ProductVariant/44001, gid://shopify/Order/5550001, gid://shopify/Location/66001). Every ID argument also takes the bare number, and objects carry legacyResourceId.
  • Lists that can grow are connections: first / after (or last / before), nodes or edges { cursor node }, pageInfo { hasNextPage endCursor } and totalCount. 20 items by default, at most 200.
  • Mutations take an input object (or a few arguments) and answer the result plus userErrors { code message field data httpStatus }. A refusal (not enough stock, a closed day) is a user error, not an exception. httpStatus is what the REST API would answer.
  • Dates are in the store’s time zone. Inputs take 2026-12-01, 2026-12-01 09:00 or 2026-12-01T09:00; answers use 2026-12-01 09:00:00.
  • Stays by the night: a product booked by the night takes checkIn and checkOut for startDate and endDate. A stay runs from the check-in day to the check-out day ([check-in, check-out)), so it needs at least one night: a same-day or reversed pick is refused with zero_nights. Quotes, availability, calendars and bookings carry nightly and nights (a booking also checkIn and checkOut); a booking keeps the rule it was sold under.
  • Money is in the store currency: floats for prices and quotes, decimal strings ("150.00") for balances and amounts that must stay exact.
  • Enums are upper case (RESERVED, SEND).
  • Nested lists (a booking’s serial numbers and moves, an order’s lines) are loaded together for the whole page, so asking for them costs no extra round trips.

Operations #

Each table gives the operation (a query or a mutation, which key may call it, and the type it returns), its arguments and what it does. Most operations take one input object: its fields are listed on the types page.

Store and caller #

OperationArgumentsWhat it does
rentalShop
query · secret or publishable key
returns RentalShop!
–The store the key belongs to: domain, name, currency, time zone, today’s date and whether online booking is ready (checkoutReady).
rentalApiCaller
query · secret or publishable key
returns RentalApiCaller!
–Who is calling: the kind of credential, a secret key’s scopes and prefix, and the apps the store has installed (installedApps). Handy to test a key.

Products, availability and prices #

Several of these also work with a storefront’s publishable key: see Headless storefronts.

OperationArgumentsWhat it does
rentalProducts
query · secret key
returns [RentalProduct!]!
query: String
first: Int = 25
Rent variants by title, variant title or SKU (at most 50). An empty query lists the first ones.
rentalProductAvailability
query · secret key
returns RentalAvailability!
input: RentalAvailabilityInput!Can this quantity be rented over these dates? The units free at the busiest moment of the range, counting bookings and live holds, turnaround included.
rentalProductCalendar
query · secret or publishable key
returns RentalCalendar!
input: RentalCalendarInput!What a date picker needs for one product: fully booked days, the booked intervals (secret keys only) and the full calendar payload the store’s own calendar uses (widgetConfig).
rentalProductPriceList
query · secret or publishable key
returns RentalPriceList!
productId: ID
variantId: ID
The rate card: every period the product is priced by, its price and its price per day.
rentalProductPrice
query · secret or publishable key
returns RentalQuote!
input: RentalPriceInput!The price of a rental over these dates, with the breakdown: exactly what the cart will charge. It prices anything that parses; availability and the store’s rules are checked when you reserve or book.
rentalProductDayPrices
query · secret or publishable key
returns RentalDayPrices!
input: RentalDayPricesInput!The price of a one-day rental starting on each day of a range (at most 366 days).
rentalCatalogAvailability
query · secret or publishable key
returns RentalCatalogAvailability!
productIds: [ID!]
handles: [String!]
selection: RentalSearchSelectionInput!
For up to 50 products (ids or handles): is each a rental, and is it free for a date search? With a place (where), how far away it is.
rentalMapMarkers
query · secret or publishable key
returns RentalMap!
collection: String
productIds: [ID!]
selection: RentalSearchSelectionInput
Map markers for rental products with a location (at most 250), optionally for a collection, some products or a place; with dates, whether each is free.

Bookings #

OperationArgumentsWhat it does
rentalBookings
query · secret key
returns RentalBookingConnection!
first: Int
after: String
last: Int
before: String
filter: RentalBookingFilterInput
sortKey: RentalBookingSortKey = ID
reverse: Boolean = true
Bookings, filtered and sorted. Newest first by default.
rentalBooking
query · secret key
returns RentalBooking
id: ID!One booking, or null when the store has no such booking.
rentalBookingDatesCheck
query · secret key
returns RentalBookingDatesCheck!
id: ID!
startDate: String!
endDate: String!
Could this booking move to new dates? The rules the new dates break, and the price difference (positive: the customer owes more).
rentalCustomerBookings
query · secret key
returns RentalCustomerBookings!
customerId: ID!A customer’s bookings: out now, coming up and past (newest first, at most 500).
rentalCustomers
query · secret key
returns [RentalCustomer!]!
query: String!
first: Int = 20
Customers who have rented before, by name or email (at least 2 letters).
createRentalBooking
mutation · secret key
returns RentalBookingCreatePayload!
input: RentalBookingCreateInput!Book a rental for a customer: the store’s rules apply, the units are held for 7 days and Shopify makes a draft order with a payment link. With block: true, block dates instead (maintenance, private use; no order).
updateRentalBooking
mutation · secret key
returns RentalBookingPayload!
id: ID!
input: RentalBookingUpdateInput!
Move a booking to new dates (it doesn’t compete with itself; force skips the stock check) or change its notes. Quantity, product and order changes belong to the Shopify order.
deleteRentalBooking
mutation · secret key
returns RentalBookingDeletePayload!
id: ID!Delete blocked dates, or a booking made through the API or an import that has no order. A booking still waiting on its draft order is cancelled and the draft deleted in Shopify.

deleteRentalBooking refuses bookings that belong to an order (has_order: cancel or refund it in Shopify), imported calendar events (imported), bookings made at checkout, at the counter or in the app (not_deletable), and a draft order that was paid meanwhile (draft_completed).

Orders #

OperationArgumentsWhat it does
rentalOrders
query · secret key
returns RentalOrderConnection!
filter: RentalOrderFilterInput
reverse: Boolean = false
first: Int
after: String
last: Int
before: String
Orders with a rental running in a window (turnaround included), each with all its rental lines. Sorted by the earliest rental start. Blocked dates and draft-order bookings are not listed.
rentalOrder
query · secret key
returns RentalOrder!
id: ID!One order’s rentals (an empty list when it has none).

Send & Return #

The same rules as the Send & Return desk. quantity defaults to the number of serial numbers, else 1. A product that tracks serial numbers needs one serial number per unit at check-out. A batch holds at most 200 items; with atomic: true (the default) one bad item refuses the whole batch with batch_refused and data.failures, and nothing is recorded. With atomic: false, the good items are recorded and the others listed in failures.

OperationArgumentsWhat it does
rentalMoveQueue
query · secret key
returns RentalMoveQueueConnection!
direction: RentalMoveDirection!
when: String = "all"
filter: RentalMoveQueueFilterInput
first: Int
after: String
last: Int
before: String
The Send & Return desk’s lists: SEND = bookings with units still to go out, by rental start; RETURN = bookings with units out, by rental end.
rentalMoveQueueCounts
query · secret key
returns RentalMoveQueueCounts!
direction: RentalMoveDirection!How many bookings each list holds: today, overdue, this week and all.
rentalMovements
query · secret key
returns RentalMovementConnection!
direction: RentalMoveDirection!
filter: RentalMovementFilterInput
sortKey: RentalMovementSortKey = DATETIME
reverse: Boolean = true
first: Int
after: String
last: Int
before: String
Every check-out (SEND) or check-in (RETURN) on record, newest first by default.
sendRentals
mutation · secret key
returns RentalMovesPayload!
input: RentalMovesInput!Check units out: one booking or a batch of up to 200, with serial numbers. A batch is all or nothing unless atomic: false.
returnRentals
mutation · secret key
returns RentalMovesPayload!
input: RentalMovesInput!Check units in, with the same rules as sendRentals.
sendRentalByScan
mutation · secret key
returns RentalScanPayload!
input: RentalScanInput!Check one unit out from a scanned code: a serial number, an order name (#1042 or 1042), a booking number or a SKU.
returnRentalByScan
mutation · secret key
returns RentalScanPayload!
input: RentalScanInput!Check one unit in from a scanned code (for a serial number: the booking it is out on).

Serial numbers #

OperationArgumentsWhat it does
rentalSerialNumbers
query · secret key
returns RentalSerialNumberConnection!
filter: RentalSerialNumberFilterInput
first: Int
after: String
last: Int
before: String
Serial numbers, by product, variant, status or part of the code.
rentalSerialNumber
query · secret key
returns RentalSerialNumber
id: ID!One serial number with its history, or null.
rentalSerialUsage
query · secret key
returns RentalSerialUsage!
input: RentalSerialUsageInputUse by serial number over a window: days out, idle days, use rate, hires and revenue. Default: the last 30 days.
rentalSerialLabels
query · secret key
returns RentalSerialLabels!
serialIds: [ID!]
productId: ID
format: String = "qr"
names: Boolean = false
A printable label sheet (PDF) for some serial numbers, or every one of a product that isn’t retired, as a link that works for 10 minutes.
rentalSerialTracking
query · secret key
returns RentalSerialTracking!
productId: ID!Whether a product tracks serial numbers, and how many it has in each status.
createRentalSerialNumber
mutation · secret key
returns RentalSerialNumberPayload!
input: RentalSerialNumberCreateInput!Add one serial number to a rent variant.
createRentalSerialNumbers
mutation · secret key
returns RentalSerialNumbersPayload!
input: RentalSerialNumberRangeInput!Add a run of serial numbers (TENT-001 to TENT-020). Codes that exist already are skipped.
updateRentalSerialNumber
mutation · secret key
returns RentalSerialNumberPayload!
id: ID!
input: RentalSerialNumberUpdateInput!
Change a serial number; only the fields you send change. Moving it to another variant is refused while it is out.
deleteRentalSerialNumber
mutation · secret key
returns RentalSerialNumberDeletePayload!
id: ID!Delete a serial number. Refused while it is out or once it has history: retire it instead.

Locations and stock #

OperationArgumentsWhat it does
rentalLocations
query · secret or publishable key
returns [RentalLocation!]!
–The store’s Shopify locations and whether rentals are picked up there. With two or more that rent, customers choose where they pick up and each product can have its own stock at each location. Storefronts see only the locations that rent, without phone numbers.
rentalProductStock
query · secret key
returns RentalProductStock!
productId: ID!A product’s rental stock: one shared stock, or units at each location.
setRentalProductStock
mutation · secret key
returns RentalProductStockPayload!
input: RentalProductStockInput!Set a product’s rental stock (a location you leave out gets 0). Per location it also takes a minimum and a maximum to keep and a price rule, and returnRules sets where customers may return the product. Refused with stock_conflict when future bookings would no longer fit, unless force: true.
rentalStockBoard
query · secret key
returns RentalStockBoard!
productId: IDUnits based at each location, free today, out with customers and on the way, per rent variant with stock at each location.
rentalTransfers
query · secret key
returns RentalTransferList!
filter: RentalTransferFilterInput
first: Int = 50
offset: Int = 0
Transfers, newest first (default: open ones: PLANNED and IN_TRANSIT).
rentalTransfer
query · secret key
returns RentalTransfer
id: ID!One transfer.
rentalTransferSuggestions
query · secret key
returns [RentalTransferSuggestion!]!
–Transfers that would bring locations below their minimum back up (from locations above their maximum). Nothing moves by itself.
createRentalTransfer
mutation · secret key
returns RentalTransferPayload!
input: RentalTransferCreateInput!Plan a transfer: it counts at once (the origin loses the units from the ship time, the destination gains them at the arrival). Refused with transfer_leaves_short (data.items: each item’s deficit) when the origin would be short for its bookings, unless force: true with a reason.
dispatchRentalTransfer
mutation · secret key
returns RentalTransferPayload!
input: RentalTransferMoveInput!Dispatch: the units leave the origin now (they must be free there). Serials scanned out travel with it.
receiveRentalTransfer
mutation · secret key
returns RentalTransferPayload!
input: RentalTransferMoveInput!Receive all or part of a dispatched transfer (items, or the serial numbers scanned in); ARRIVED when everything is in.
cancelRentalTransfer
mutation · secret key
returns RentalTransferPayload!
id: ID!Cancel: a planned transfer stops counting; a dispatched one is turned back (what is still on the way goes back to the origin).
setRentalLocationRules
mutation · secret key
returns RentalLocationPayload!
locationId: ID!
rules: JSON!
Set a location’s own rules: hours, closed days, turnaround, lead time, price rule (see RentalLocation.rules). Keys left out keep their value; null clears one (the store’s setting applies).

Rental kits #

OperationArgumentsWhat it does
rentalKits
query · secret key
returns RentalKitConnection!
first: Int
after: String
last: Int
before: String
The store’s rental kits, oldest first.
rentalKit
query · secret or publishable key
returns RentalKit
id: ID!One kit, or null.
createRentalKit
mutation · secret key
returns RentalKitPayload!
input: RentalKitCreateInput!Make a kit: a new Shopify product (active, on no sales channel yet: publish it in Shopify) rented as its items.
updateRentalKit
mutation · secret key
returns RentalKitPayload!
id: ID!
input: RentalKitUpdateInput!
Change a kit’s title, items, pricing or rules. Existing bookings keep the items they were booked with.
deleteRentalKit
mutation · secret key
returns RentalKitDeletePayload!
id: ID!Remove a kit (refused while a booking of it isn’t over). Its Shopify product is archived.

Delivery & pickup #

OperationArgumentsWhat it does
rentalHandoverWindows
query · secret or publishable key
returns [RentalHandoverWindow!]!
input: RentalHandoverWindowsInput!The time slots of a hand-over day for a product and method (pick-up, delivery or transport).
rentalDeliverySettings
query · secret key
returns JSON!
–The store’s delivery & pickup settings as JSON (the same object the app’s settings page saves).
updateRentalDeliverySettings
mutation · secret key
returns RentalDeliverySettingsPayload!
settings: JSON!Change the delivery & pickup settings: what you send is merged over the current settings.
changeRentalHandover
mutation · secret key
returns RentalBookingPayload!
input: RentalHandoverChangeInput!Change a booking’s method, return method, time slots or return tracking. Refused with not_available when the new transit days are booked, unless force.
rentalDeliveryCheck
query · secret or publishable key
returns RentalDeliveryCheck!
postcode: String!
productId: ID
variantId: ID
locationId: ID
Can delivery be booked to a postcode, and from which area and location.
rentalDeliveryZones
query · secret key
returns [RentalDeliveryZone!]!
–The store’s delivery areas, in order.
rentalSchedule
query · secret key
returns RentalSchedule!
date: String
locationId: ID
method: RentalHandoverMethod
One day’s run sheet (default today, store time); locationId narrows to one location, method to one way.
createRentalDeliveryZone
mutation · secret key
returns RentalDeliveryZonePayload!
input: RentalDeliveryZoneInput!Add a delivery area. Once a store has areas, delivery needs a postcode inside one.
updateRentalDeliveryZone
mutation · secret key
returns RentalDeliveryZonePayload!
id: ID!
input: RentalDeliveryZoneInput!
Change a delivery area (the fields sent change).
deleteRentalDeliveryZone
mutation · secret key
returns RentalDeliveryZoneDeletePayload!
id: ID!Remove a delivery area (bookings keep their postcode).

Balances (pay part now, rest later) #

paymentUrl (the customer’s payment link) is only given to secret keys. Keep it private.

OperationArgumentsWhat it does
rentalBalances
query · secret key
returns RentalBalanceConnection!
first: Int
after: String
last: Int
before: String
status: RentalBalanceStatus
dueBefore: String
Balances of rentals paid in part at checkout. Default: due and overdue, due first.
rentalBalance
query · secret key
returns RentalBalance
id: ID!One balance, or null.
sendRentalBalanceInvoice
mutation · secret key
returns RentalBalancePayload!
input: RentalBalanceInvoiceInput!Send Shopify’s invoice email with the payment link (not_open once it is paid or waived).
markRentalBalancePaid
mutation · secret key
returns RentalBalancePayload!
balanceId: ID!Record a balance as paid outside the checkout (cash). outstanding_differs when the order owes more than the balance.
waiveRentalBalance
mutation · secret key
returns RentalBalancePayload!
balanceId: ID!
note: String
Waive a balance: it comes off the order (or its invoice is deleted).

Subscriptions (rentals billed per period) #

A rental can be billed per period instead of for fixed dates: ongoing until it comes back, or for a set number of periods. The subscription belongs to a booking; the unit stays held until it is returned or the term ends. Start one with reserveRentalDates and a subscription (see the storefront API).

OperationArgumentsWhat it does
rentalSubscriptions
query · secret key
returns RentalSubscriptionConnection!
first: Int
after: String
last: Int
before: String
status: RentalSubscriptionStatus
customerId: ID
Rentals billed per period: overdue first, then ending, then by next due date.
rentalSubscription
query · secret key
returns RentalSubscription
id: ID!
endRentalSubscription
mutation · secret key
returns RentalSubscriptionPayload!
input: RentalSubscriptionEndInput!End the rental: due back by returnBy (default the end of the paid period); billing by invoice stops then. Another app’s subscription must be cancelled in that app too.
recordRentalSubscriptionPayment
mutation · secret key
returns RentalSubscriptionPayload!
input: RentalSubscriptionPaymentInput!Record a payment for the next unpaid period (cash, bank transfer, a renewal order that wasn’t matched).

POS alerts #

A rent item rung up at the point of sale without the Rentals desk has no dates and no booking. It raises an alert until the merchant books the dates or dismisses it.

OperationArgumentsWhat it does
rentalPosAlerts
query · secret key
returns [RentalPosAlert!]!
status: RentalPosAlertStatus
first: Int
Rent items sold at POS without the desk, newest first (default: OPEN).
resolveRentalPosAlert
mutation · secret key
returns RentalPosAlertPayload!
id: ID!
bookingId: ID
note: String
Close a POS alert: with a booking made some other way (bookingId; its order line is attached when it has none), or dismissed with a note.

Calendar sync (iCal) #

A feed’s url is its only credential: anyone with it can read the feed. Only the host of an imported calendar’s address is shown, because the full address carries the booking channel’s secret.

OperationArgumentsWhat it does
rentalCalendarSync
query · secret key
returns RentalCalendarSync!
productId: ID!A product’s calendar sync: its feed addresses, its rent variants and the calendars it imports.
rentalCalendarImports
query · secret key
returns [RentalCalendarImport!]!
productId: IDThe calendars the store imports, for all products or one.
rotateRentalCalendarFeed
mutation · secret key
returns RentalCalendarSyncPayload!
productId: ID!
flavour: RentalCalendarFeedFlavour = AVAILABILITY
variantId: ID
Give a feed a new address; the old one stops working at once.
setRentalCalendarBookingsFeed
mutation · secret key
returns RentalCalendarSyncPayload!
productId: ID!
enabled: Boolean!
variantId: ID
Turn the bookings feed (every booking with its customer) on or off.
createRentalCalendarImport
mutation · secret key
returns RentalCalendarImportPayload!
productId: ID!
input: RentalCalendarImportInput!
Import another calendar (Airbnb, Booking.com, Google …): its events block the dates. It is read at once unless syncNow: false.
updateRentalCalendarImport
mutation · secret key
returns RentalCalendarImportPayload!
id: ID!
input: RentalCalendarImportUpdateInput!
Change an imported calendar; only the fields you send change.
deleteRentalCalendarImport
mutation · secret key
returns RentalCalendarImportDeletePayload!
id: ID!Stop importing a calendar and free the dates it blocked.
syncRentalCalendarImport
mutation · secret key
returns RentalCalendarImportPayload!
id: ID!Read an imported calendar now.

Rental settings and API keys #

OperationArgumentsWhat it does
rentalSettingsCsv
query · secret key
returns String!
–Every rent variant’s settings as CSV text. Edit it and send it back with importRentalSettings.
importRentalSettings
mutation · secret key
returns RentalSettingsImportPayload!
csv: String!
dryRun: Boolean = true
Import rental settings from CSV. dryRun (the default) reports what would change and writes nothing; dryRun: false applies the rows without errors.
rentalApiKeys
query · secret key
returns [RentalApiKey!]!
–The store’s API keys, active ones first (never a secret).
createRentalApiKey
mutation · secret key
returns RentalApiKeyCreatePayload!
input: RentalApiKeyInput!Make an API key. The secret is in the answer this once only. A key can make keys, but never one with more access than its own.
revokeRentalApiKey
mutation · secret key
returns RentalApiKeyPayload!
id: ID!Revoke an API key at once (revoking twice is harmless). A key may revoke itself.

Examples #

Today’s check-outs, then send one with its serial numbers #

curl -s "https://YOUR-API-ADDRESS/graphql/2026-10" -H "Authorization: Bearer sirb_your_secret_key" -H "Content-Type: application/json" \
  -d '{"query": "{ rentalMoveQueueCounts(direction: SEND) { today overdue } rentalMoveQueue(direction: SEND, when: \"today\", first: 50) { edges { due quantity serials node { id orderName productName } } } }"}'
mutation Send($items: [RentalMoveItemInput!]!) {
  sendRentals(input: {items: $items, atomic: true}) {
    processed
    items { booking { id quantitySent status } serials outstanding }
    failures { index bookingId code message }
    userErrors { code message data }
  }
}
# variables: {"items": [{"bookingId": "gid://sirentals/Booking/42", "serials": ["TENT-001", "TENT-003"]}]}

A refused batch answers userErrors: [{code: "batch_refused", data: {failures: [{index, booking_id, code, message, status}]}}] and records nothing.

Block dates for maintenance, then free them #

mutation Block {
  createRentalBooking(input: {variantId: "44001", startDate: "2026-12-01", endDate: "2026-12-03", quantity: 1, block: true, notes: "Annual service"}) {
    booking { id legacyResourceId startDate endDate status source }
    userErrors { code message data }
  }
}
# then, with the id it answered:
mutation Free {
  deleteRentalBooking(id: "gid://sirentals/Booking/43") { deletedBookingId userErrors { code message } }
}

A booking with its serial numbers, moves and order #

query Booking {
  rentalBooking(id: "42") {
    id orderName status state startDate endDate quantity quantitySent quantityReturned
    customer { name email }
    serials { code out }
    movements { direction datetime quantity serials }
    location { name }
  }
}

Orders with a rental next week #

query NextWeek {
  rentalOrders(filter: {startDate: "2026-12-07", endDate: "2026-12-13"}, first: 50) {
    totalCount
    nodes { name status earliestStart customer { name } bookings { productName quantity startDate endDate status } }
  }
}

Rotate your own key #

mutation Rotate {
  createRentalApiKey(input: {name: "Warehouse scanner 2026", scopes: ["read", "write"]}) {
    apiKey { id prefix scopes }
    secret                     # shown this once: store it, deploy it
    userErrors { code message }
  }
}
# once the new key is in use:
# mutation { revokeRentalApiKey(id: "gid://sirentals/ApiKey/3") { apiKey { revokedAt } userErrors { code } } }

Quote a stay by the night #

query Stay {
  rentalProductPrice(input: {variantId: "44001", checkIn: "2026-12-04", checkOut: "2026-12-07", quantity: 1}) {
    nightly nights lengthUnit total currency
    lines { label kind amount }
  }
  rentalProductCalendar(input: {variantId: "44001", startDate: "2026-12-01", endDate: "2026-12-31"}) {
    nightly turnoverDates
    stayDays { date state checkIn checkOut }
  }
}

Move stock between locations #

query Board {
  rentalStockBoard {
    locations { id publicName }
    rows { title total locations { locationId units free out inTransit belowMin } }
  }
  rentalTransferSuggestions { title fromLocationId toLocationId quantity reason }
}
mutation Plan {
  createRentalTransfer(input: {fromLocationId: "66001", toLocationId: "66002", shipAt: "2026-12-02", arriveAt: "2026-12-03", items: [{variantId: "44001", quantity: 4}]}) {
    transfer { id status items { variantId quantity } }
    userErrors { code message data }     # transfer_leaves_short: the origin would be short; force: true with a reason
  }
}
# then: dispatchRentalTransfer(input: {id: "gid://sirentals/StockTransfer/9"}), receiveRentalTransfer(input: {id: "…"}) at the other end

Can we deliver to this postcode, and what is on the run sheet #

query Delivery {
  rentalDeliveryCheck(postcode: "SW1A 1AA", variantId: "44001") {
    ok postcode locationId code message earliestStart
    zone { id name }
  }
  rentalSchedule(date: "2026-12-04") {
    day deliveries { orderName customerName addressText window items { title quantity } }
    collections { orderName addressText window }
    pickups { orderName } returns { orderName } toPost { orderName } dueBack { orderName late }
    route mapLinks
  }
}

End a subscription and record a payment #

query Live {
  rentalSubscriptions(first: 20, status: PAST_DUE) {
    nodes { id orderName status interval periodPrice currency paidThrough nextDue returnBy }
  }
}
mutation Stop {
  endRentalSubscription(input: {subscriptionId: "gid://sirentals/Subscription/5", returnBy: "2026-12-31", note: "Customer asked"}) {
    subscription { status returnBy }
    userErrors { code message }
  }
}
mutation Paid {
  recordRentalSubscriptionPayment(input: {subscriptionId: "gid://sirentals/Subscription/6", amount: "45.00", note: "Bank transfer"}) {
    subscription { status paidThrough nextDue }
    userErrors { code message }
  }
}