View Categories

Launching soon on the Shopify App StoreJoin the waitlist

GraphQL API reference: Rentals & Bookings

13 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.
  • 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. 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). Refused with stock_conflict when future bookings would no longer fit, unless force: true.

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 posted).
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.

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).

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 } } }