Contents
On this page
Create Booking - Plugixa Car Rental
POST /plugixa-car-rental/v1/bookings
Make a booking as staff: the dates, the car model and, if you like, a particular car. It is priced on the server and holds its car until it is confirmed or cancelled.
The user needs the plugixa_car_rental_create capability.
Staff bookings are not bound by the shortest notice or the furthest horizon of the booking rules: a pickup in the past is accepted. Opening hours, closures and the shortest and longest rental still apply.
Note. A coupon that does not apply (unknown, expired, used up, too short a rental) does not refuse the request: the booking is made at full price and nothing in the response says so. Check
discount_total, or try the code with Quote Booking first, which reports it innotices.coupon.
Note. With
confirm: truethe booking is written first and confirmed second. If the confirmation is refused, the answer is that error, but the booking exists,pending, and still holds its car. List pending bookings or cancel it with Change Booking Status.
Request
POST https://your-site.example/wp-json/plugixa-car-rental/v1/bookings
Request body
Content type: application/json
| Field | Type | Required | Description |
|---|---|---|---|
pickup_location_id |
integer |
Yes | A published location that allows pickups. |
return_location_id |
integer |
No | A published location that allows returns. Omitted or 0: the pickup location. |
pickup_at |
string |
Yes | Local time at the pickup location: YYYY-MM-DD HH:MM, with optional seconds, a space or a T between. No time-zone offset, and a bare date is refused. |
return_at |
string |
Yes | Local time at the return location, in the same form. After pickup_at. |
vehicle_model_id |
integer |
Yes | A published vehicle model. |
vehicle_unit_id |
integer |
No | A particular car of that model, free for the dates. Omitted: the free car with the lowest odometer reading is given. |
customer_name |
string |
No | Customer’s name. |
customer_email |
string |
No | Must be a valid address when given. |
customer_phone |
string |
No | Customer’s phone. |
notes |
string |
No | Plain text. |
confirm |
boolean |
No | Confirm the booking in the same request. Defaults to false: the booking stays pending. |
extras |
object[] |
No | Extras to add, as {"id": 3, "quantity": 1}. Only published extras count, and a quantity above the extra’s max_quantity is cut to it. Needs the Extras app. |
coupon |
string |
No | A coupon code. Needs the Coupons app. |
driver_age |
integer |
No | The driver’s age, 16 to 99, for the young and senior driver surcharges and each model’s minimum age. Needs Pro with the Driver age app. |
delivery_address |
string |
No | Deliver the car to this address. Needs Pro with the Delivery and collection app. |
collection_address |
string |
No | Collect the car from this address at the end. Needs Pro with the Delivery and collection app. |
currency |
string |
No | Price in another of your currencies, by its code. Needs Pro with the Multi-currency app. Unknown codes are ignored. |
The price is worked out on the server from the stored rates, extras, coupon and rules, never taken from the request. Two requests for the last free car of a model are served one after the other, so only one gets it. A car is free when no confirmed or picked-up booking, and no pending booking whose hold is still running, overlaps the dates plus the turnaround time between rentals, and it is not blocked in the calendar. Custom field answers cannot be set here: they are taken by the booking form’s checkout only.
{
"pickup_location_id": 1,
"pickup_at": "2026-12-01 10:00",
"return_at": "2026-12-04 10:00",
"vehicle_model_id": 4,
"customer_name": "Claire Martin",
"customer_email": "claire.martin@example.com",
"customer_phone": "+33 6 12 34 56 78",
"notes": "Arrives on the 08:40 train.",
"extras": [
{
"id": 3,
"quantity": 1
}
],
"coupon": "WINTER10",
"confirm": true
}
Responses
201 Created. data is the booking, with its lines.
{
"success": true,
"data": {
"id": 128,
"reference": "CR-2026-0128",
"status": "confirmed",
"source": "admin",
"vehicle_model_id": 4,
"vehicle_unit_id": 9,
"pickup_location_id": 1,
"return_location_id": 1,
"pickup_at": "2026-12-01 09:00:00",
"return_at": "2026-12-04 09:00:00",
"hold_expires_at": null,
"billable_days": 3,
"customer_id": null,
"customer_name": "Claire Martin",
"customer_email": "claire.martin@example.com",
"customer_phone": "+33 6 12 34 56 78",
"currency": "EUR",
"subtotal": 219.0,
"discount_total": 21.9,
"tax_total": 0.0,
"total": 197.1,
"deposit": 800.0,
"amount_paid": 0.0,
"payment_status": "unpaid",
"payment_method": "",
"notes": "Arrives on the 08:40 train.",
"meta": [],
"created_by": 1,
"modified_by": 1,
"created_at": "2026-11-12 09:30:00",
"modified_at": "2026-11-12 09:30:00",
"deleted_at": null,
"line_items": [
{
"id": 301,
"booking_id": 128,
"position": 0,
"type": "rental",
"reference_id": 5,
"label": "Peugeot 3008, 3 days",
"quantity": 3.0,
"unit_price": 65.0,
"total": 195.0,
"meta": null,
"created_by": "1",
"modified_by": "1",
"created_at": "2026-11-12 09:30:00",
"modified_at": "2026-11-12 09:30:00",
"deleted_at": null
},
{
"id": 302,
"booking_id": 128,
"position": 1,
"type": "extra",
"reference_id": 3,
"label": "Child seat",
"quantity": 1.0,
"unit_price": 24.0,
"total": 24.0,
"meta": null,
"created_by": "1",
"modified_by": "1",
"created_at": "2026-11-12 09:30:00",
"modified_at": "2026-11-12 09:30:00",
"deleted_at": null
},
{
"id": 303,
"booking_id": 128,
"position": 2,
"type": "coupon",
"reference_id": 7,
"label": "Coupon WINTER10",
"quantity": 1.0,
"unit_price": -21.9,
"total": -21.9,
"meta": null,
"created_by": "1",
"modified_by": "1",
"created_at": "2026-11-12 09:30:00",
"modified_at": "2026-11-12 09:30:00",
"deleted_at": null
}
],
"next": [
"picked_up",
"cancelled",
"no_show"
],
"pickup_local": "2026-12-01 10:00 CET",
"return_local": "2026-12-04 10:00 CET",
"vehicle_model_name": "Peugeot 3008",
"vehicle_unit_plate": "AB-123-CD",
"pickup_location_name": "Paris Gare de Lyon",
"return_location_name": "Paris Gare de Lyon"
}
}
| Field | Type | Meaning |
|---|---|---|
data.id |
integer |
The booking’s id |
data.reference |
string |
Reference shown to the customer: the prefix, the year and the id |
data.status |
string |
See booking statuses |
data.next |
string[] |
Statuses it may move to next |
data.source |
string |
admin (staff or this API) or public (the booking form) |
data.vehicle_model_id |
integer |
The model booked |
data.vehicle_unit_id |
integer|null |
The car given to it |
data.pickup_at |
string |
UTC datetime of the pickup |
data.return_at |
string |
UTC datetime of the return |
data.pickup_local |
string|null |
The pickup on the pickup location’s clock, with its zone abbreviation |
data.return_local |
string|null |
The return on the return location’s clock |
data.hold_expires_at |
string|null |
UTC datetime a public hold runs out. null for staff bookings and once confirmed |
data.billable_days |
integer |
Days charged |
data.customer_id |
integer|null |
Always null in this version |
data.subtotal |
number |
Sum of the positive lines |
data.discount_total |
number |
Discounts, as a positive number |
data.tax_total |
number |
Tax |
data.total |
number |
What the customer pays |
data.deposit |
number |
Held at the desk, not part of total |
data.amount_paid |
number |
Payments minus refunds |
data.payment_status |
string |
unpaid, partially_paid, paid or refunded |
data.payment_method |
string |
The payment method chosen at checkout, or empty |
data.meta |
object|array |
Extra details: fields (answers to your custom fields), consent_at, page_url, cancellation once a customer cancels, manage_nonce once a manage link was made. An empty list [] when there is none |
data.line_items |
object[] |
The price lines, in order. See the note below |
data.vehicle_model_name |
string|null |
Name of the model |
data.vehicle_unit_plate |
string|null |
Plate of the car |
data.pickup_location_name |
string|null |
Name of the pickup location |
data.return_location_name |
string|null |
Name of the return location |
Each line in line_items is stored as its own record: id, booking_id, position, type, reference_id, label, quantity, unit_price and total, plus meta, a JSON string of anything else the line carried, or null. Its created_by and modified_by come back as strings, not integers.
Errors
| Status | Code | When |
|---|---|---|
422 |
plugixa_car_rental_unknown_location |
A location is missing, unpublished or in the trash. data.field names it. |
422 |
plugixa_car_rental_location_not_allowed |
The pickup location takes no pickups, or the return location takes no returns. |
422 |
plugixa_car_rental_invalid_datetime |
pickup_at or return_at is missing, a bare date, not a date, or a time skipped by a clock change at that location. |
422 |
plugixa_car_rental_return_before_pickup |
return_at is not after pickup_at. |
422 |
plugixa_car_rental_location_closed |
The location is closed on that day. |
422 |
plugixa_car_rental_outside_hours |
The time is outside the location’s opening hours and it charges no after-hours fee. |
422 |
plugixa_car_rental_too_short |
The rental is shorter than the shortest rental in the booking rules. |
422 |
plugixa_car_rental_too_long |
The rental is longer than the longest rental in the booking rules. data.field is return_at. |
422 |
plugixa_car_rental_unknown_model |
vehicle_model_id is missing, unpublished or not a model. |
422 |
plugixa_car_rental_invalid_email |
customer_email is not a valid address. |
422 |
plugixa_car_rental_not_priced |
No published rate covers a rental of this length for the model. |
422 |
plugixa_car_rental_refused |
A module refused this car for this request, for example a driver younger than the model allows. data.field names the field. |
409 |
plugixa_car_rental_unit_unavailable |
vehicle_unit_id is not free for those dates. |
409 |
plugixa_car_rental_sold_out |
No car of the model is free for those dates. |
409 |
plugixa_car_rental_busy |
Another booking for the same model was being made at that moment. Try again. |
422 |
plugixa_car_rental_too_long |
A customer field is longer than its column. data.field and data.max say which and how long. |
With confirm: true, the errors of Change Booking Status to confirmed can come back too.
Plus the errors every endpoint can return.
Examples
curl -s -X POST "https://your-site.example/wp-json/plugixa-car-rental/v1/bookings" \
-u "admin:$WP_APP_PASSWORD" \
-H "Content-Type: application/json" \
-d '{"pickup_location_id":1,"pickup_at":"2026-12-01 10:00","return_at":"2026-12-04 10:00","vehicle_model_id":4,"customer_name":"Claire Martin","customer_email":"claire.martin@example.com","customer_phone":"+33 6 12 34 56 78","notes":"Arrives on the 08:40 train.","extras":[{"id":3,"quantity":1}],"coupon":"WINTER10","confirm":true}'
<?php
$ch = curl_init('https://your-site.example/wp-json/plugixa-car-rental/v1/bookings');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_USERPWD => 'admin:' . getenv('WP_APP_PASSWORD'),
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode([
'pickup_location_id' => 1,
'pickup_at' => '2026-12-01 10:00',
'return_at' => '2026-12-04 10:00',
'vehicle_model_id' => 4,
'customer_name' => 'Claire Martin',
'customer_email' => 'claire.martin@example.com',
'customer_phone' => '+33 6 12 34 56 78',
'notes' => 'Arrives on the 08:40 train.',
'extras' => [
[
'id' => 3,
'quantity' => 1,
],
],
'coupon' => 'WINTER10',
'confirm' => true,
]),
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
const auth = Buffer.from(`admin:${process.env.WP_APP_PASSWORD}`).toString('base64');
const response = await fetch('https://your-site.example/wp-json/plugixa-car-rental/v1/bookings', {
method: 'POST',
headers: {
'Authorization': `Basic ${auth}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
pickup_location_id: 1,
pickup_at: '2026-12-01 10:00',
return_at: '2026-12-04 10:00',
vehicle_model_id: 4,
customer_name: 'Claire Martin',
customer_email: 'claire.martin@example.com',
customer_phone: '+33 6 12 34 56 78',
notes: 'Arrives on the 08:40 train.',
extras: [{ id: 3, quantity: 1 }],
coupon: 'WINTER10',
confirm: true,
}),
});
const { data } = await response.json();
Quick Links
Share