Contents
Create Booking - Plugixa Car Wash
POST /plugixa-carwash/v1/bookings
Book a wash on behalf of a customer. The booking is priced on the server and given a free bay or team, exactly as the staff app does.
Needs the plugixa_carwash_create capability.
Note. A staff booking is not held to the opening hours, the time grid, the minimum notice or the booking window: any date and time is accepted as long as a suitable bay or team is free then. Use Free Times for Staff to offer only the times a customer could book.
Note.
customer_idis ignored. The customer is found by
Request
POST https://your-site.example/wp-json/plugixa-carwash/v1/bookings
Request body
Content type: application/json
| Field | Type | Required | Description |
|---|---|---|---|
location_id |
integer |
Yes | A published location. |
vehicle_type_id |
integer |
When any exist | A published vehicle type. Required as soon as one vehicle type is published. |
package_id |
integer |
No | A published package. Send a package, service_ids, or both. |
service_ids |
array |
No | Published service ids, as an array or a comma-separated string. Staff may add any published service, add-on or not. |
date |
string |
Yes | Y-m-d, in the location’s time zone. |
time |
string |
Yes | H:i, in the location’s time zone. |
resource_id |
integer |
No | A bay or team at this location. Left out, the first suitable free one is chosen. |
status |
string |
No | pending or confirmed. Anything else, or nothing, means confirmed. |
email |
string |
Yes | A valid e-mail address. Picks or creates the customer. |
first_name |
string |
No | Cut to 100 characters. |
last_name |
string |
No | Cut to 100 characters. |
phone |
string |
No | Cut to 50 characters. |
company |
string |
No | Cut to 190 characters. |
vehicle_make |
string |
No | Cut to 120 characters. Remembered on the customer’s vehicles. |
vehicle_plate |
string |
No | Cut to 30 characters. Remembered on the customer’s vehicles. |
address |
string |
No | Cut to 255 characters. |
message |
string |
No | A note from the customer. |
staff_notes |
string |
No | Internal notes. |
coupon |
string |
No | A coupon code. A code that does not apply refuses the booking. |
payment_method |
string |
No | A payment method id such as cash or bank_transfer. Left out, the first enabled method is recorded when something is due. Not checked against the enabled methods for staff. |
The price comes from the catalogue, never from the request: package or service price, the vehicle type’s fee and any per-vehicle price, the location’s tax rate, and coupons. The duration is worked out the same way, and the bay is held for it plus the location’s buffer. Pro apps such as Memberships and Fleet accounts can change the price as well.
{
"location_id": 3,
"vehicle_type_id": 2,
"package_id": 4,
"date": "2026-11-14",
"time": "09:00",
"first_name": "Lucas",
"last_name": "Bernard",
"email": "lucas@example.com",
"phone": "+1 217 555 0199",
"vehicle_make": "Toyota RAV4",
"vehicle_plate": "ABC1234",
"staff_notes": "Regular customer"
}
Responses
201 Created. data is the new booking.
{
"success": true,
"data": {
"id": 42,
"number": "CW2026-0042",
"form_id": null,
"location_id": 3,
"resource_id": 5,
"customer_id": 25,
"customer_vehicle_id": 31,
"vehicle_type_id": 2,
"package_id": 4,
"start_at": "2026-11-14 15:00:00",
"end_at": "2026-11-14 16:00:00",
"blocked_until": "2026-11-14 16:10:00",
"duration": 60,
"status": "confirmed",
"hold_expires_at": null,
"first_name": "Lucas",
"last_name": "Bernard",
"email": "lucas@example.com",
"phone": "+1 217 555 0199",
"company": "",
"vehicle_make": "Toyota RAV4",
"vehicle_plate": "ABC1234",
"address": "",
"message": "",
"agreements": [],
"currency": "USD",
"subtotal": 49.0,
"discount": 0.0,
"coupon_code": "",
"tax": 3.06,
"total": 52.06,
"payment_method": "cash",
"payment_status": "unpaid",
"source": "staff",
"page_url": "",
"staff_notes": "Regular customer",
"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,
"items": [
{
"role": "package",
"item_id": 4,
"name": "Deluxe",
"price": 49.0,
"duration": 60
}
],
"events": [
{
"from": "",
"to": "confirmed",
"note": "Booked by staff",
"user_id": 1,
"created_at": "2026-11-12 09:30:00"
}
],
"next": [
"in_progress",
"completed",
"cancelled",
"no_show"
],
"location": {
"id": 3,
"name": "Riverside"
},
"local_date": "2026-11-14",
"local_time": "09:00",
"resource": {
"id": 5,
"name": "Bay 1",
"color": "#0EA5E9"
},
"payments": []
}
}
| Field | Type | Meaning |
|---|---|---|
data.id |
integer |
The booking’s id |
data.number |
string |
The booking reference, CW + year + id, for example CW2026-0042 |
data.location_id |
integer |
The location |
data.resource_id |
integer|null |
The bay or mobile team it is booked on |
data.customer_id |
integer|null |
The customer record it belongs to |
data.vehicle_type_id |
integer|null |
The vehicle type chosen |
data.package_id |
integer|null |
The package chosen, null for services only |
data.start_at |
string |
UTC datetime the wash starts |
data.end_at |
string |
UTC datetime the wash ends |
data.blocked_until |
string |
UTC datetime the bay is free again: end_at plus the location’s buffer |
data.duration |
integer |
Minutes |
data.status |
string |
hold, pending, confirmed, in_progress, completed, cancelled or no_show |
data.hold_expires_at |
string|null |
For a hold (waiting for an online payment): when it is cancelled if still unpaid |
data.agreements |
array |
The booking form’s agreements as {text, accepted}. Empty for a staff booking |
data.subtotal |
number |
Sum of the lines, discounts included |
data.discount |
number |
Total of the discount lines |
data.coupon_code |
string |
The coupon used, or empty |
data.tax |
number |
Tax, at the location’s rate |
data.total |
number |
What the booking costs |
data.payment_status |
string |
unpaid, paid, partially_paid or refunded |
data.source |
string |
online or staff |
data.staff_notes |
string|null |
Internal notes, never shown to the customer |
data.modified_at |
string |
UTC datetime of the last change |
data.items |
array |
The priced lines: role (package, addon, service, fee or discount), item_id, name, price, duration |
data.events |
array |
Every status change: from, to, note, user_id, created_at |
data.next |
array |
The statuses it can move to from here |
data.location |
object|null |
id and name |
data.local_date |
string |
The start date in the location’s time zone, Y-m-d |
data.local_time |
string |
The start time in the location’s time zone, H:i |
data.resource |
object|null |
The bay or team: id, name, color |
data.payments |
array |
Payments and refunds recorded against it. See List Booking Payments |
Errors
| Status | Code | When |
|---|---|---|
422 |
plugixa_carwash_not_offered |
No such published location, vehicle type, package or service. The message says which: “Choose a location.”, “Choose your vehicle type.”, “Choose a package.” |
422 |
plugixa_carwash_nothing_chosen |
Neither a package nor a service. |
422 |
plugixa_carwash_not_for_vehicle |
The package or a service is switched off for this vehicle type. |
422 |
plugixa_carwash_invalid_time |
date or time is missing or not in the right format. |
422 |
plugixa_carwash_invalid_email |
email is missing or not valid. |
422 |
plugixa_carwash_invalid_resource |
resource_id is not a bay or team at this location. |
422 |
plugixa_carwash_invalid_coupon |
The coupon code does not apply. The message says why. |
409 |
plugixa_carwash_slot_taken |
The chosen bay, or every suitable bay, is booked at that time. |
503 |
plugixa_carwash_busy |
Too many bookings were being made at that location at once. Try again. |
500 |
plugixa_carwash_insert_failed |
The database refused the booking. |
Plus the errors every endpoint can return.
Examples
curl -s -X POST "https://your-site.example/wp-json/plugixa-carwash/v1/bookings" \
-u "admin:$WP_APP_PASSWORD" \
-H "Content-Type: application/json" \
-d '{"location_id":3,"vehicle_type_id":2,"package_id":4,"date":"2026-11-14","time":"09:00","first_name":"Lucas","last_name":"Bernard","email":"lucas@example.com","phone":"+1 217 555 0199","vehicle_make":"Toyota RAV4","vehicle_plate":"ABC1234","staff_notes":"Regular customer"}'
<?php
$ch = curl_init('https://your-site.example/wp-json/plugixa-carwash/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([
'location_id' => 3,
'vehicle_type_id' => 2,
'package_id' => 4,
'date' => '2026-11-14',
'time' => '09:00',
'first_name' => 'Lucas',
'last_name' => 'Bernard',
'email' => 'lucas@example.com',
'phone' => '+1 217 555 0199',
'vehicle_make' => 'Toyota RAV4',
'vehicle_plate' => 'ABC1234',
'staff_notes' => 'Regular customer',
]),
]);
$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-carwash/v1/bookings', {
method: 'POST',
headers: {
'Authorization': `Basic ${auth}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
location_id: 3,
vehicle_type_id: 2,
package_id: 4,
date: '2026-11-14',
time: '09:00',
first_name: 'Lucas',
last_name: 'Bernard',
email: 'lucas@example.com',
phone: '+1 217 555 0199',
vehicle_make: 'Toyota RAV4',
vehicle_plate: 'ABC1234',
staff_notes: 'Regular customer',
}),
});
const { data } = await response.json();
Quick Links
Share