Contents
On this page

Create Booking - Plugixa Boat Rental

POST /pbr/v1/bookings

Create a booking for one boat, checked and priced the way a booking made in the office is.

An API key PRO with Read and write access, or a signed-in user with the plugixa_boatrental_create capability.

Note. A booking made here skips the booking widget’s own rules: it is not held to a booking form, and it is not refused because the marina or the boat is marked inactive. It is still refused for a past or too-distant departure, closed hours, a window the boat does not allow, too many guests, or no free boat.

Request

POST https://your-site.example/wp-json/pbr/v1/bookings

Request body

Content type: application/json

Field Type Required Description
location_id integer Yes The marina. Must exist.
boat_id integer Yes The boat. Must exist.
start string Yes Departure, YYYY-MM-DD HH:MM (seconds optional) on the marina’s clock. start_datetime is accepted too, and wins when both are sent.
end string Yes Return, in the same form. end_datetime is accepted too. Must be after the departure.
guests integer No People on board, at least 1 (the default). More than the boat carries is refused.
unit_id integer No One particular unit of the boat, which must be free. Without it, the first free unit is picked for you.
customer_name string No The customer’s name.
customer_email string No Must be an email address when sent.
customer_phone string No The customer’s phone.
notes string No Notes, line breaks kept.
status string No The status to start in, one of the site’s booking statuses. Without it, the site’s default status applies (pending unless changed).
billing_type string No How the booking is priced: hour, half_day, day, week or seat. Without it, the boat’s own unit.
user_id integer No A WordPress customer account to file the booking under. Without it, the booking has no account.
billing object No Billing details as a flat map of plain values.
meta object No Answers to the booking form’s own fields, keyed by field.
booking_form_id integer No The booking form the answers in meta belong to, so each answer keeps its label.
gateway string No Stored as the booking’s payment method. Defaults to offline.

payment_status and amount_paid cannot be sent: they come from the payments ledger, and sending either is refused. Record money with Record Payment.

A booking is always for one boat: a quantity is checked for being a positive number and otherwise ignored. The total and the line items are worked out from the boat’s prices for the window, the same way the booking widget prices it. The booking is recorded with the source admin, and the usual booking emails and booking.created webhooks follow.

{
  "location_id": 2,
  "boat_id": 5,
  "start": "2026-11-20 10:00",
  "end": "2026-11-20 14:00",
  "guests": 4,
  "customer_name": "Ana Silva",
  "customer_email": "ana@example.com",
  "customer_phone": "+351 912 345 678",
  "status": "confirmed"
}

Responses

201 Created. data is the new booking.

{
  "success": true,
  "data": {
    "id": 412,
    "booking_ref": "BOAT-7F3A91",
    "language_code": null,
    "location_id": 2,
    "boat_id": 5,
    "unit_id": 9,
    "booking_form_id": null,
    "user_id": null,
    "status": "confirmed",
    "billing_type": "hour",
    "start_datetime": "2026-11-20 10:00:00",
    "end_datetime": "2026-11-20 14:00:00",
    "start_utc": "2026-11-20 10:00:00",
    "end_utc": "2026-11-20 14:00:00",
    "buffer_before": 0,
    "buffer_after": 30,
    "block_start_utc": "2026-11-20 10:00:00",
    "block_end_utc": "2026-11-20 14:30:00",
    "quantity": 1,
    "guests": 4,
    "source": "admin",
    "hold_expires_at": null,
    "customer_name": "Ana Silva",
    "customer_email": "ana@example.com",
    "customer_phone": "+351 912 345 678",
    "billing": null,
    "currency": "EUR",
    "subtotal": 380.0,
    "discount_total": 0.0,
    "tax_total": 0.0,
    "deposit_amount": 0.0,
    "total": 380.0,
    "amount_paid": 0.0,
    "payment_method": "offline",
    "payment_status": "unpaid",
    "notes": null,
    "meta": null,
    "created_by": 0,
    "modified_by": 0,
    "created_at": "2026-11-12 09:30:00",
    "updated_at": null,
    "modified_at": "2026-11-12 09:30:00",
    "deleted_at": null,
    "items": [
      {
        "id": 977,
        "booking_id": 412,
        "type": "rental",
        "reference_id": 5,
        "label": "1 boat × 4 hours",
        "quantity": 4.0,
        "unit_price": 95.0,
        "total": 380.0,
        "tax_rate_id": null,
        "meta": null
      }
    ],
    "custom_fields": [],
    "location_name": "Lisbon Marina",
    "boat_name": "Sea Ray 230"
  }
}
Field Type Meaning
data.id integer The new booking’s id
data.booking_ref string The reference the customer sees
data.unit_id integer|null The unit the booking holds
data.status string The status sent, or the site’s default
data.hold_expires_at string|null For a pending booking on a site with a hold time: when it stops holding the boat
data.total number The priced total
data.items array The lines the booking was priced as: type (rental, fee, or a line a Pro app adds), label, quantity, unit_price, total
data.custom_fields array The answers in meta, each as key, label and value
data.source string Always admin here

Errors

Status Code When
422 missing_fields location_id, boat_id, the departure or the return is missing.
422 plugixa_boatrental_invalid_location location_id is not an existing marina.
404 not_found boat_id is not an existing boat.
422 plugixa_boatrental_derived_field payment_status or amount_paid was sent.
422 plugixa_boatrental_invalid_date A departure or return is not a real date.
422 plugixa_boatrental_invalid_choice status or billing_type is not one of the allowed values. data.allowed lists them.
422 plugixa_boatrental_not_a_number boat_id, guests, unit_id or quantity is not a number.
422 plugixa_boatrental_invalid_guests guests is below 1. Also plugixa_boatrental_invalid_boat_id and plugixa_boatrental_invalid_quantity.
422 plugixa_boatrental_invalid_email customer_email is not an email address.
422 plugixa_boatrental_too_long A text field is longer than its column holds. data.max says how long it may be.
422 invalid_range The return is not after the departure.
422 too_soon / too_far The departure is in the past, closer than the site’s minimum notice, or beyond its booking horizon.
422 location_closed / outside_hours The marina is closed that day, or the departure or return is outside its hours.
422 too_many_guests More guests than the boat carries.
422 not_a_slot / not_a_day A boat with fixed departures was asked for another time, or a full-day boat for more than one day.
422 too_short / too_long / bad_step The window breaks the boat’s minimum, maximum or step.
409 no_availability No free unit of the boat for that window, or unit_id is taken.
409 busy Another booking for the same boat was being written. Try again.

Plus the errors every endpoint can return.

Examples

curl -s -X POST "https://your-site.example/wp-json/pbr/v1/bookings" \
  -H "Authorization: Bearer $PBR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"location_id":2,"boat_id":5,"start":"2026-11-20 10:00","end":"2026-11-20 14:00","guests":4,"customer_name":"Ana Silva","customer_email":"ana@example.com","customer_phone":"+351 912 345 678","status":"confirmed"}'
<?php
$ch = curl_init('https://your-site.example/wp-json/pbr/v1/bookings');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST  => 'POST',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER     => [
        'Authorization: Bearer ' . getenv('PBR_API_KEY'),
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS     => json_encode([
        'location_id'    => 2,
        'boat_id'        => 5,
        'start'          => '2026-11-20 10:00',
        'end'            => '2026-11-20 14:00',
        'guests'         => 4,
        'customer_name'  => 'Ana Silva',
        'customer_email' => 'ana@example.com',
        'customer_phone' => '+351 912 345 678',
        'status'         => 'confirmed',
    ]),
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
const response = await fetch('https://your-site.example/wp-json/pbr/v1/bookings', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${process.env.PBR_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    location_id: 2,
    boat_id: 5,
    start: '2026-11-20 10:00',
    end: '2026-11-20 14:00',
    guests: 4,
    customer_name: 'Ana Silva',
    customer_email: 'ana@example.com',
    customer_phone: '+351 912 345 678',
    status: 'confirmed',
  }),
});
const { data } = await response.json();

Quick Links

Share