A booking deposit lets a customer pay part of the rental price at checkout and the rest, the balance, later: from a payment link, charged to their saved card, or at pick-up. This page covers the GraphQL fields that go with it: showing the offer in a cart, letting the shopper choose, reading a booking’s payment schedule, paying the balance from a headless storefront, and the back-office operations that list, collect, waive and charge balances.
The booking deposit is part of the rental price. It is not the refundable security deposit, which the Waiver & Damage add-on holds separately.
Booking deposits are off until the store turns them on under Stores > Configuration > Sales Igniter > Rental > Booking deposit. While they are off, Cart.rental_payment is null and no balances exist. Everything here is GraphQL; there is no other API for it.
Two halves #
| Half | Fields | Who can call it |
|---|---|---|
| Storefront (customer) | Cart.rental_paymentsetRentalPaymentChoiceCustomerOrder.rental_paymentstartRentalBalancePayment | A guest or customer token for the cart fields. The order field and startRentalBalancePayment need the customer token of the customer who placed the booking. |
| Back office | rentalBalancesrentalBalanceRentalOrder.paymentsendRentalBalanceInvoice, markRentalBalancePaidwaiveRentalBalance, chargeRentalBalance | An admin or integration token holding the ACL resources in the table below. |
Permissions #
| Field | ACL resource | Where that is in the admin role tree |
|---|---|---|
rentalBalances, rentalBalance, payment on rentalOrders | SalesIgniter_Rental::balance | Rental > Booking balances: view |
sendRentalBalanceInvoice, markRentalBalancePaid, waiveRentalBalance, chargeRentalBalance | SalesIgniter_Rental::balance_manage | Rental > Booking balances: record, send link, charge, waive |
balance_manage sits under balance in the role tree, so a role that can manage balances can also view them. A token without the right resource gets a graphql-authorization error. RentalOrder.payment is the one exception: it returns null instead of an error when the token lacks SalesIgniter_Rental::balance, so an existing rentalOrders query keeps working for a role that cannot see balances.
The offer on a cart #
Ask the cart how it can be paid with rental_payment. It is null when booking deposits are off for the store. CartPrices.grand_total always equals due_today for the current choice, so a storefront that shows the cart’s grand total shows the right amount to pay now.
query CartPayment {
cart(cart_id: "Zk3x9QJ0example") {
prices { grand_total { value currency } }
rental_payment {
offered
choice
always
full_total { value currency }
due_today { value currency }
due_later { value currency }
due_at
due_label
collect
auto_ok
consent
label_full
label_part
reason
message
}
}
}
{
"data": {
"cart": {
"prices": { "grand_total": { "value": 100, "currency": "USD" } },
"rental_payment": {
"offered": true,
"choice": "PART",
"always": false,
"full_total": { "value": 400, "currency": "USD" },
"due_today": { "value": 100, "currency": "USD" },
"due_later": { "value": 300, "currency": "USD" },
"due_at": "2026-10-23 09:00:00",
"due_label": "Fri 23 Oct",
"collect": "LINK",
"auto_ok": false,
"consent": null,
"label_full": "Pay in full ($400.00)",
"label_part": "Pay 25% now ($100.00), $300.00 on Fri 23 Oct",
"reason": null,
"message": null
}
}
}
}
The wording of label_full, label_part, consent and message is the store’s own and follows the store view’s language; the example text above is only an illustration.
RentalCartPayment #
| Field | Type | Meaning |
|---|---|---|
offered | Boolean! | True when this cart can be paid with a booking deposit. The shopper may still pay in full. |
choice | RentalPaymentChoice! | FULL or PART: what the cart is set to now. |
always | Boolean! | True when the store’s Who chooses setting is Always take a deposit, so there is no choice to show. |
full_total | Money! | The order total, paid in full. |
due_today | Money! | What the shopper pays at checkout with the current choice. |
due_later | Money! | The balance: the rental share, its tax and, when the store says so, shipping. Zero when not offered. |
due_at | String | When the balance is due, store time, Y-m-d H:i:s. |
due_label | String | The due date as the cart shows it, such as Tue 29 Sep. |
collect | RentalBalanceCollect | How the balance will be collected. |
auto_ok | Boolean! | True when the balance will be charged to the card used at checkout. The shopper agrees to that with the consent sentence. |
consent | String | The consent sentence to show beside the choice when auto_ok is true. |
label_full | String | The pay-in-full option, with its amount. |
label_part | String | The deposit option: today’s amount, the balance and its date. |
reason | String | Why no deposit is offered: off, multishipping, extension, no_rental_rows, free, below_minimum, starts_soon, no_balance, payment_method, engine_missing, error. |
message | String | A sentence for the shopper when the reason deserves one, such as a rental that starts too soon. |
Only rental rows that qualify are split. Other rows in the cart, a buy-out line for example, are paid in full today. Rental extensions and subscription rentals are never split.
Letting the shopper choose #
setRentalPaymentChoice sets the cart to pay in full or to pay a deposit now. It returns the whole cart, so ask for prices and rental_payment in the same call and redraw. With the store’s Always take a deposit setting the choice is ignored: the cart stays on PART whenever a deposit is offered.
mutation Choose {
setRentalPaymentChoice(input: { cart_id: "Zk3x9QJ0example", choice: FULL }) {
cart {
prices { grand_total { value currency } }
rental_payment { choice due_today { value currency } due_later { value currency } }
}
}
}
{
"data": {
"setRentalPaymentChoice": {
"cart": {
"prices": { "grand_total": { "value": 400, "currency": "USD" } },
"rental_payment": {
"choice": "FULL",
"due_today": { "value": 400, "currency": "USD" },
"due_later": { "value": 0, "currency": "USD" }
}
}
}
}
}
The rest of checkout is unchanged: setShippingAddressesOnCart, setPaymentMethodOnCart and placeOrder work as normal, and the order is placed for the amount due today. A missing cart_id or choice is a graphql-input error.
| Type | Fields |
|---|---|
SetRentalPaymentChoiceInput | cart_id: String! (the masked cart id), choice: RentalPaymentChoice! |
SetRentalPaymentChoiceOutput | cart: Cart! |
A booking’s payment schedule #
CustomerOrder.rental_payment gives a signed-in customer the same schedule My Account shows. It is null for an order paid in full and for any order that is not the caller’s own.
query MyBookingPayment {
customer {
orders(pageSize: 5) {
items {
number
rental_payment {
paid_today { value currency }
security_deposit { value currency }
balance { value currency }
due_at
due_label
status
collect
message
can_pay
pay_url
}
}
}
}
}
{
"data": {
"customer": {
"orders": {
"items": [
{
"number": "000000123",
"rental_payment": {
"paid_today": { "value": 100, "currency": "USD" },
"security_deposit": { "value": 50, "currency": "USD" },
"balance": { "value": 300, "currency": "USD" },
"due_at": "2026-10-23 09:00:00",
"due_label": "Fri 23 Oct",
"status": "SCHEDULED",
"collect": "LINK",
"message": "The balance is due on Fri 23 Oct.",
"can_pay": true,
"pay_url": "https://your-store.com/salesigniter_rental/balance/pay?token=..."
}
}
]
}
}
}
}
| Field | Type | Meaning |
|---|---|---|
paid_today | Money! | What was paid at checkout, the security deposit left out. |
security_deposit | Money | The refundable security deposit paid at checkout, when the booking has one. |
balance | Money! | The balance, its tax share included. |
due_at | String | When the balance is due, store time. |
due_label | String | The due date as My Account shows it. |
status | RentalBalanceStatus! | Where the balance stands (see the status table). |
collect | RentalBalanceCollect! | How the balance is collected. |
message | String | One sentence about where the balance stands. |
can_pay | Boolean! | True when the customer can pay the balance now, also before the due date. |
pay_url | String | A signed payment link, valid 14 days. It is made when you ask for the field, so leave it out of queries that do not need it. null when there is nothing to pay. |
Paying the balance from a headless storefront #
startRentalBalancePayment builds a cart that holds the balance and makes it the signed-in customer’s cart. Send the customer through your normal checkout with the returned cart_id; the balance is a separate order placed through the standard checkout, so every payment method’s own steps (3-D Secure, wallets, redirects) work unchanged. The customer’s previous cart comes back once the balance order is placed. It is allowed at any time the balance is owed, before or after the due date.
mutation PayBalance {
startRentalBalancePayment(order_number: "000000123") {
cart_id
}
}
{ "data": { "startRentalBalancePayment": { "cart_id": "Qm7aBalanceCartExample" } } }
It needs the customer token of the customer who placed the booking. Errors:
| Category | When |
|---|---|
graphql-authorization | No customer token, or the order belongs to someone else. |
graphql-no-such-entity | No such order for this customer. |
graphql-input | The order number is empty, the balance is no longer owed, or a payment for it is already being processed. |
Guests have no account to sign in to. They pay from the emailed payment link instead.
Back-office reads #
rentalBalances #
Lists balances, soonest due first, like Rental > Send and Return > Balances in the admin. Requires SalesIgniter_Rental::balance. Every filter is optional.
query OverdueBalances {
rentalBalances(filter: { queue: OVERDUE }, pageSize: 20, currentPage: 1) {
total_count
items {
id
order_number
customer_name
customer_email
currency
amount
due_local
collect
status
queue
attempts
next_attempt_at
last_error
rental_start
}
page_info { page_size current_page total_pages }
}
}
{
"data": {
"rentalBalances": {
"total_count": 1,
"items": [
{
"id": 41,
"order_number": "000000123",
"customer_name": "Alex Example",
"customer_email": "[email protected]",
"currency": "USD",
"amount": 300,
"due_local": "2026-10-23 09:00:00",
"collect": "AUTO",
"status": "OVERDUE",
"queue": "OVERDUE",
"attempts": 3,
"next_attempt_at": null,
"last_error": "The card was declined.",
"rental_start": "2026-10-30 10:00:00"
}
],
"page_info": { "page_size": 20, "current_page": 1, "total_pages": 1 }
}
}
}
| Argument | Type | Notes |
|---|---|---|
filter | RentalBalanceFilterInput | See below. |
pageSize | Int | Default 20, at most 300. |
currentPage | Int | Default 1, 1-based. |
| Filter field | Type | Finds |
|---|---|---|
queue | RentalBalanceQueue | The balances in one of the Balances screen’s quick filters. |
status | [RentalBalanceStatus!] | Balances in any of these statuses. |
collect | RentalBalanceCollect | Balances collected this way. |
due_from, due_to | String | Due on or after, and on or before, this UTC day, YYYY-MM-DD. |
order_number | String | One booking. |
customer_email | String | One customer. |
rentalBalance #
The balance of one booking, found by its order number. Returns null when the booking was paid in full at checkout. Requires SalesIgniter_Rental::balance.
query OneBalance {
rentalBalance(order_number: "000000123") {
id order_id order_number amount base_amount tax currency
due_at due_local collect status queue
paid_via paid_at balance_order_id
}
}
RentalOrder.payment returns the same RentalBalance on a rentalOrders result, so a picking list can show who still owes money. See Retrieving Orders by Rental Date.
RentalBalance #
| Field | Type | Meaning |
|---|---|---|
id | Int! | The balance’s id. |
order_id | Int! | The booking order’s entity id. |
order_number | String! | The booking order’s number. |
customer_name, customer_email | String | The customer. |
currency | String | The order currency the balance is owed in. |
amount | Float! | The balance in the order currency, its tax share included. |
base_amount | Float! | The balance in the base currency. |
tax | Float! | The tax inside amount. |
due_at | String | When the balance is due, UTC. |
due_local | String | When the balance is due, store time. |
collect | RentalBalanceCollect! | How it is collected. |
status | RentalBalanceStatus! | Where it stands. |
queue | RentalBalanceQueue! | The Balances screen quick filter the row is in. |
attempts | Int! | Automatic charge attempts so far. |
next_attempt_at | String | The next automatic attempt, UTC, after a soft decline. |
last_error | String | The last failure, as staff see it. |
paid_via | String | online, auto, counter_cash, counter_card, counter_bank, counter_other or waived. |
paid_at | String | UTC, Y-m-d H:i:s. |
balance_order_id | Int | The order that paid the balance, or the pending one waiting for an offline payment. |
rental_start, rental_end | String | The earliest start and the latest end on the booking, store time. |
Dates are Y-m-d H:i:s. Fields called due_at, paid_at and next_attempt_at are UTC; due_local, rental_start and rental_end are the store’s time. Money in RentalBalance is a plain number in the currency named by currency, not a Money object.
Back-office operations #
All four return the updated RentalBalance. They need SalesIgniter_Rental::balance_manage. A business-rule refusal (the balance is paid already, it is being collected right now, and so on) is a graphql-input error with a plain sentence in message.
| Mutation | What it does | Arguments |
|---|---|---|
sendRentalBalanceInvoice | Emails the customer a signed payment link for the balance, valid 14 days. | order_number: String! |
markRentalBalancePaid | Records a balance paid outside the store (cash, card terminal, bank transfer). Places the balance order with the offline Paid at the counter method and invoices it. | input: MarkRentalBalancePaidInput! |
waiveRentalBalance | Writes the balance off. The booking keeps what was paid. | input: WaiveRentalBalanceInput! |
chargeRentalBalance | Charges the saved card now. Only for balances collected automatically. A decline is not an error: it comes back in status and last_error. | order_number: String! |
mutation Counter {
markRentalBalancePaid(input: { order_number: "000000123", method: CASH, reference: "Receipt 4471" }) {
order_number
status
paid_via
paid_at
balance_order_id
}
}
{
"data": {
"markRentalBalancePaid": {
"order_number": "000000123",
"status": "PAID",
"paid_via": "counter_cash",
"paid_at": "2026-10-30 09:42:10",
"balance_order_id": 987
}
}
}
mutation Waive {
waiveRentalBalance(input: { order_number: "000000123", reason: "Goodwill after a late delivery" }) {
order_number
status
paid_via
}
}
mutation Charge {
chargeRentalBalance(order_number: "000000123") {
order_number
status
attempts
next_attempt_at
last_error
}
}
| Input | Fields |
|---|---|
MarkRentalBalancePaidInput | order_number: String!, method: RentalCounterPaymentMethod!, reference: String (a receipt or transfer reference, kept on the balance order) |
WaiveRentalBalanceInput | order_number: String!, reason: String (written into the order comment) |
Refunds, including Refund booking, are not part of this API. Use the admin order screen for them.
Enumerations #
| Enum | Values |
|---|---|
RentalPaymentChoice | FULL pay in full now; PART pay a booking deposit now and the balance later. |
RentalBalanceCollect | LINK the customer is emailed a payment link; AUTO charged to the saved card on the due date; PICKUP paid at the counter at pick-up. |
RentalCounterPaymentMethod | CASH, CARD (a card terminal), BANK (a bank transfer), OTHER. |
RentalBalanceStatus #
| Value | Meaning |
|---|---|
SCHEDULED | The deposit is paid; the balance is not due yet. |
DUE | Due now, collected by link or at pick-up. |
CHARGING | An automatic charge is running, or waiting for the payment gateway to confirm. |
PAID | The balance is paid. |
FAILED | The automatic charge was declined. See next_attempt_at for the next try. |
NEEDS_ACTION | The bank asked the customer to confirm the payment. |
OVERDUE | Past due and still unpaid. |
WAIVED | Written off by staff. |
CANCELLED | The booking was cancelled or refunded, so nothing is owed. |
RELEASED | The booking was released for non-payment. |
RentalBalanceQueue #
The quick filters of the Balances screen. Each balance is in exactly one.
| Value | Meaning |
|---|---|
DUE_THIS_WEEK | Due within the week. |
OVERDUE | Overdue. |
FAILED | The automatic charge failed. |
WAITING | Waiting for the customer to confirm a payment. |
SCHEDULED | Not due yet. |
PAID_30_DAYS | Paid in the last 30 days. |
CLOSED | Paid earlier, waived, cancelled or released. |
Errors #
| Category | When |
|---|---|
graphql-authorization | The token lacks the ACL resource, or there is no customer token where one is needed. |
graphql-input | A missing or empty argument, an unknown method, or a balance that cannot take the action (for example it is already paid, or a charge is running). |
graphql-no-such-entity | The order number does not exist. |
For tokens, dates, paging and error shapes shared by every field, see GraphQL API Overview & Authentication.
