Making a booking
Three calls make a booking: check availability, get a quote, then book.
1. Availability
Code
One row per car type with how many cars are free over the span, and per day. The count already includes the rental's overbooking allowance, and drops to the physical fleet inside the rental's lead-time window. The span is 1 to 120 days.
2. Quote
Code
Send the car type, the pickup and return, the locations, the extras and a coupon code if any. The answer is every line the customer will pay, with tax per line, and the total. A quote is a value: it is not stored, and nothing is held by it.
A quote can carry problems, in problems:
| Problem | What it means | Stops a booking |
|---|---|---|
no_rate | Nothing prices that car type on those days. | Yes |
too_short | Shorter than the rental's minimum stay (minStayDays on GET /rentals). | Yes |
too_long | Kept for later. No quote gives it today. | Yes |
bad_dates | A date does not read, or the return is not after the pickup. | Yes |
coupon_unknown | No such coupon code. | No |
coupon_expired | The pickup is outside the code's dates. | No |
coupon_not_open | The code cannot be redeemed on the day of booking. | No |
coupon_not_applicable | The code does not cover this car type, length or extras. | No |
A coupon problem leaves the booking possible without the discount. Show it to the customer. When the coupon applied, the quote's coupon names it: code, and percentBp (1000 is 10%) or amountMinor, the other null. It is null otherwise. Each of terms has discountMinor, what the coupon took off that term's total, so totalMinor plus discountMinor is the price before the coupon.
The minimum stay is one number for the whole rental. A car type also shows minRentalHours and maxRentalDays, but no quote and no booking checks them today. A booking longer than maxRentalDays is priced and taken.
The driver's age
Send driverAge when you know it: whole years, from 16 to 120. A rental's young driver fee goes on only with it. Without it, there is no fee. An age outside that range answers 422 with invalid.
A car type can have a minimum driver age, its minDriverAge. A driver under it answers 422 with the code driver_too_young, on a quote and on a booking alike. Nothing is booked. Every key gets this answer, the organization's own key too.
Insurance, excess and deposit
A quote says what the customer pays at most if something happens to the car. liability names every peril, such as collision, theft and gravel, with its excess in minor units after every cover on the quote. 0 is covered in full. Null is not covered: the customer pays the whole cost, up to the car's value. depositMinor is what the rental holds on the card at pickup.
Ask a quote for each level or package the customer can pick, in one POST /quotes call, and show the excess and the deposit side by side. The booking made from the same order keeps the same excess and deposit.
An included cover that the customer may refuse has includedByDefault and declinable true on GET /rentals/{slug}/products. Send its id in declined, on the quote as on the booking. The quote then shows the price and the excess without it. Any other id answers 422 with invalid.
On the products, a cover also has exclusions, what it does not cover, in the asked locale when the rental translated them. insurer names the insurer when the cover is an insurer's policy, else it is null.
Many rentals in one call
A group site that sells several rentals asks about all of them at once. A reseller has its own paths, with prices and holds: see Sell as a reseller.
Code
/availability gives the answer above for every rental the key reaches, one block per rental slug. rentals= limits it to some of them.
/quotes takes up to 50 quotes as {"quotes": [...]}. Each one is the body of a single quote plus rental, the slug. The answers come in the order asked. An answer has data with the quote, or error: not_found when the rental or car type is not there or nothing prices it, driver_too_young for a driver under the car type's minimum age. One failed quote leaves the others alone.
Car type and location ids belong to one rental. Read them per rental from /rentals/{slug}/car-types and /rentals/{slug}/locations, and keep them.
3. Book
Code
Send the same fields as the quote plus the customer. The booking is priced again at that moment and the price frozen on it, so a rate change later never rewrites it. The answer is 201 with the booking.
Always send an Idempotency-Key. A retry with the same key and the same body gets the first answer again, so a retry after a timeout never makes two bookings. A 500 is the exception: the key then stays in progress for 24 hours. Errors says what to do. A 403 or a 404 is not kept under the key, so the same key works again once the cause is gone. The same key with a different body is refused with 409 and the code idempotency_mismatch. A retry while the first call still runs gets 409 with in_progress: wait a moment and try again.
When the car type is full, the answer is 409 with the code none_free. Nothing is booked.
When the booking cannot be priced, the answer is 422. The code is quote when the quote had a problem that stops a booking, and the message names it. A driver under the car type's minimum age answers 422 with driver_too_young.
The booking keeps the driver's age you sent. A change later prices the booking again with that age, so a young driver fee stays on the booking.
The customer
customer takes firstName and lastName, and email, phone and country if you have them. country is the country the customer lives in, as two letters, such as DE. It counts for a customer the booking adds, and a customer the rental already has keeps the country on file. The booking answers customer.country, or null when the rental has none. A customer the rental already knows by that email and the same first and last name is the same customer. The email alone makes a new customer.
Send marketingOptIn: true only when the customer ticked a box to get offers from the rental. The rental keeps the time and that it came through the API. It counts for a customer the booking adds. A customer the rental already has keeps the answer on file. false, or no field, changes nothing on file.
Custom fields
A rental can add its own fields to bookings and to customers, such as a voucher number or a member number. The rental picks which of them go through the API, with Send through the API on the field. Only those fields are read and written here, and only by the rental's own keys. A reseller's key and the reseller routes do not carry them.
A booking carries customFields, and its customer carries customFields too. Each is an object by the field's key. A value nobody gave reads as the field's default, or null.
Code
Send the same two objects when you make a booking. A value has the JSON type of its field:
| Field type | Value |
|---|---|
| Text, long text | A string. |
| Link | A string that starts with http. |
| Number | A number. |
| Yes or no | true or false. |
| One of a list | The value of one choice, as a string. |
| Several of a list | A list of choice values. |
| Date | A string, as 2026-06-01. |
These answer 422 with invalid, and no booking is made. The message names each field:
- A key that is no field of the rental, or a field the rental does not send through the API.
- A value of the wrong type, or one that is not among the choices.
- A required field with no default that is left out, on the booking or on the customer.
The customer's fields count only for a customer the booking adds. A customer the rental already has keeps the values on file. A required customer field must come with every booking all the same: the rental finds the customer only after it holds the car.
While the rental's plan has no booking and customer fields, both objects are empty and every key is refused.
Who referred the customer
A customer who came through an affiliate's link lands on your site with ?mref= in the address. Keep it for the session and send it back as mref on the booking, as it came. An unknown or expired one is ignored, never refused.
When your site knows the affiliate but has no mref, send their code as affiliate. A code outside the rental's program is ignored. The API never tells you whom a booking was attributed to: that is the rental's business.
Warnings
A booking carries warnings: what the rental should know before it hands over a car. The same customer already has a car over these dates, an earlier booking ended owing money, a chargeback, a habit of cancelling. None of them stops anything. A key of one channel sees only what the bookings of that channel show. The warnings are for the people who sell and hand over the car. Never show them to the customer.
Payment
Code
Starts a payment at the rental's own processor. No card data passes through this API. It needs a key with the payment.take scope.
mode: "link"answers with aurl. Send the customer there. They land back onreturnUrl, or on the rental's own return address.mode: "embedded"answers withsession, the values that processor's own browser code takes. Draw the form on your own site.kind: "hold"places a deposit instead of taking the money, where the processor allows it.amountleft out: a sale asks forpayNowMinor, the balance less the fees paid on arrival. When only those fees are left, it asks for the balance. A hold asks for the deposit. When nothing is due, the answer is422withinvalid.
The payment is only started here. It counts on the booking when the processor tells us it happened. Read the booking again, or listen for the booking.payment webhook.
A rental with no online processor answers 409 with no_processor. A link with no return address answers 409 with no_return_url. A processor that refuses answers 409 with processor, and the message is the processor's.
Only while reserved
A change or a cancel through the API works only while the booking is reserved. The rule is the same for every key: a website's, an own reseller's, the organization's and a reseller on Mellow's. Once the car is out, or the booking is returned, closed, cancelled or a no-show, the API answers 409 with the code wrong_status.
A later change, such as a longer rental, goes through the rental. The customer's manage booking page then shows how to reach the rental.
Cancelling
Code
Send a reason if you have one. Only a reserved booking can be cancelled. Any other status answers 409 with the code wrong_status.
Changing
Code
Send only what changes: pickupAt, returnAt, pickupLocationId, returnLocationId, carTypeId, customFields, and a reason if you have one. The booking is priced again over its new span by the same rules as a new one, and the extras it already has come along. The answer is the booking as it now stands.
A change can refuse:
409withnone_free: no car of that car type is free for the new span. The booking is unchanged.409withwrong_status: the booking is no longer reserved, so the API cannot change it.409withmanual_price: the rental priced the booking by hand. Only the rental changes it.409withbelow_paid: the new total is below what is paid. A refund is the rental's decision. The new total counts the charges the rental added by hand, which a change keeps.422withdriver_too_young: the new car type needs an older driver than the age sent with the booking.422: the new span cannot be priced, and themessagesays why.
customFields on a change holds only the fields that change. A key left out keeps its value. null takes a value off, but not from a required field. A change of custom fields alone prices nothing. The customer's own fields do not change here. See Custom fields.
A paused channel
The rental can pause a reseller's channel on the reseller's page. Its key still reads its bookings. Availability and a quote answer 403 with the code paused, and so do a hold, a new booking, a change, a cancel and a check-in mark. Across rentals, /availability and /quotes leave a paused rental out, or answer paused for it.
Reading
GET /rentals/{slug}/bookings lists the bookings on your channel: 100, or up to 200 with limit. They come newest pickup first, then newest made. from and to pick the bookings that touch those days. GET /rentals/{slug}/bookings/{id} reads one. A key never sees another channel's bookings: those answer 404.