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 in notices.coupon.

Note. With confirm: true the 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