Contents
On this page
Plugixa Car Rental REST API - Plugixa Car Rental
The integration surface for Plugixa Car Rental: bookings you can search, price, make and move through their statuses, their payments, invoices and inspections, the fleet (categories, models and cars), locations, rate plans, extras and coupons you can list, create, edit and delete, the Pro pricing rules, tax rates, pricing calendar and rentals report, and the public routes the booking form and the customer booking page use. Enough to sync bookings into another system, feed a desk app or a BI tool, or build your own booking front end.
Everything the admin app, the booking form and the customer booking page do, they do over this API, under the namespace plugixa-car-rental/v1. Every rule is enforced at the route, not in the page.
This is the API the plugin’s own screens use. It is not a separately versioned public API, and the plugin makes no promise that routes, parameters or response shapes stay the same between versions. Pin the plugin version you build against, and test after updating.
Base URL
https://your-site.example/wp-json/plugixa-car-rental/v1
On a site with plain permalinks, pass the route as a parameter instead:
https://your-site.example/?rest_route=/plugixa-car-rental/v1/bookings
Authentication
There are two kinds of route.
Staff routes are everything except the public routes below. The plugin adds no authentication of its own and has no API keys: it relies on whatever WordPress provides, then checks the capabilities of the user WordPress identified. From a script, use a WordPress Application Password (created under Users -> Profile -> Application Passwords) for an account that holds the capabilities below:
curl -s "https://your-site.example/wp-json/plugixa-car-rental/v1/bookings" \
-u "admin:$WP_APP_PASSWORD"
| Caller | How it signs in |
|---|---|
| A script or another system | An Application Password, or any other method WordPress accepts for REST requests |
| The admin app | The logged-in cookie plus a REST nonce in X-WP-Nonce, the normal WordPress way |
| The booking form and the customer booking page | Nothing: they call the public routes |
Capabilities
| Capability | What it allows |
|---|---|
plugixa_car_rental_view |
Read records: every list and single record, searches and quotes, the payment ledger, invoices, inspections, the pricing calendar and the report |
plugixa_car_rental_create |
Create records and bookings, record a desk payment, issue an invoice |
plugixa_car_rental_edit |
Change records, change a booking’s status, save and sign inspections |
plugixa_car_rental_delete |
Move records to the trash, restore them, delete them for good, refund a payment |
plugixa_car_rental_export |
Export lists to CSV (not part of this reference) |
plugixa_car_rental_manage_settings |
Read and save the settings and payment credentials (not part of this reference) |
plugixa_car_rental_manage |
Open the admin app. Never needs granting: anyone who holds one of the six above has it |
Activation grants all of them to the Administrator role. A user with the WordPress manage_options capability is always treated as holding every one. They are ordinary WordPress capabilities, so any role editor can grant them to other roles. A dedicated integration user with only plugixa_car_rental_view, for example, can read everything and change nothing.
Every check passes through the plugixa_car_rental_user_can filter, so a site can narrow it per kind of record or per record. See Hooks.
Apps
A route belongs to an app in the App manager. Bookings, Fleet, Locations, Pricing (rate plans) and Payments are core apps and are always on. The others can be switched off, and an app that is off takes its routes with it: they answer 404 rest_no_route. Each endpoint page names the app it needs. The Pro routes exist only when Plugixa Car Rental Pro is active.
Responses
A successful staff response wraps its payload:
{
"success": true,
"data": { "id": 1, "name": "Paris Gare de Lyon" },
"meta": { "total": 4, "pages": 1 }
}
meta is only present on lists. Send request bodies as JSON with Content-Type: application/json. Ids and counts are integers and money amounts are numbers. created_at, modified_at and deleted_at are UTC strings in the form 2026-11-12 09:30:00, and so are a booking’s pickup_at and return_at. Pickup and return times you send are the opposite: local time at the location, YYYY-MM-DD HH:MM, without an offset. See Create Booking.
Updates
PUT and PATCH work the same way on every record: only the fields you send are changed, and the others keep their stored value.
Edit conflicts
Every record carries modified_at. When an update sends back the modified_at it read, the plugin compares it with the stored one and answers 409 plugixa_car_rental_conflict if they differ: somebody else saved the record in between. The error’s data carries expected and current.
The check is opt-in: an update that sends no modified_at is not checked. Two saves inside the same second cannot be told apart. A script that reads, changes and writes back a record should send modified_at so it never overwrites a change made by staff in the meantime.
Lists and pagination
Every list endpoint accepts the same parameters, and puts the totals in meta, not in headers (X-WP-Total is not sent).
| Parameter | Type | Default | Description |
|---|---|---|---|
page |
integer |
1 |
Page to return, from 1 |
per_page |
integer |
50 |
Rows per page, 1 to 200 |
search |
string |
Substring search over the columns each endpoint lists | |
orderby |
string |
id |
One of the sortable columns each endpoint lists. Anything else falls back to id |
order |
string |
desc |
asc or desc |
view |
string |
trash lists trashed records only |
Filtering lists
Besides search, a list can be narrowed field by field with query parameters of the form <field>__<operator>=<value>:
GET /plugixa-car-rental/v1/bookings?status__in=confirmed,picked_up&pickup_at__between=2026-12-01,2026-12-31
| Operator | Meaning |
|---|---|
eq, ne |
Equal, not equal |
gt, gte, lt, lte |
Greater or less than, for dates |
between |
Two values, comma-separated, both included |
in |
Any of several values, comma-separated |
contains |
Text contains the value |
empty, not_empty |
The field has no value, or has one. The value sent is ignored |
Which fields and operators a list accepts depends on the resource, and each list page names its fields. Every list can be filtered on created_at and modified_at. Dates are YYYY-MM-DD on the site’s calendar: between takes in the whole of both days. Asking for a field the list does not have, or an operator that does not work on it, is refused with a 400 rather than ignored, so a typo never quietly returns a wider list.
Trash
DELETE on a record moves it to the trash; it stops appearing in lists and single reads, and comes back with view=trash on its list. A vehicle model, a car or a location that upcoming bookings still use cannot be trashed, nor a model that still has cars, a location that is still a car’s home, or a category that still holds models: the answer is 409 plugixa_car_rental_in_use, with the reasons as sentences in data.blockers. Each Delete page says what stops it.
Two more routes exist for every resource that has a Delete endpoint:
| Method | Route | What it does |
|---|---|---|
POST |
/<resource>/{id}/restore |
Bring the record back from the trash. Answers {"restored": true} in data (a booking answers with the booking itself) |
DELETE |
/<resource>/{id}/purge |
Delete it for good. Answers {"purged": true} in data (a booking answers {"id": ...}) |
Both need plugixa_car_rental_delete. <resource> is the path of the resource’s list, for example vehicle-units or rate-plans. An id that does not exist, or a restore of a record that is not in the trash, answers 404 plugixa_car_rental_not_found (a booking answers 409 plugixa_car_rental_restore_failed). Restoring a car, a location or a coupon whose plate, VIN or code another live record has taken since is refused with 409, and so is a rate plan whose days now overlap another rate.
Bookings are stricter: a booking can only be purged from the trash (409 plugixa_car_rental_not_trashed otherwise), and a booking that ever had a payment completed on it is never purged (409 plugixa_car_rental_kept): it stays in the trash as a financial record. Its price lines go with it.
Note. For every resource except bookings, purge does not require the record to be in the trash first, and it skips the checks that stop a trash: a location that is a car’s home, or a model with upcoming bookings, is deleted for good without complaint. Trash a record first and purge it from the trash.
Endpoints
Staff routes, for a signed-in user with the capabilities above:
| Resource | Endpoints |
|---|---|
| Bookings | List, Create, Get, Change status, Delete, Search availability, Quote |
| Payments | List, Record, Refund |
| Invoices | Get, Issue, Download PDF PRO |
| Inspections PRO | List, Update, Sign, Download signed PDF |
| Vehicle categories | List, Create, Get, Update, Delete |
| Vehicle models | List, Create, Get, Update, Delete |
| Vehicle units (cars) | List, Create, Get, Update, Delete |
| Locations | List, Create, Get, Update, Delete |
| Rate plans | List, Create, Get, Update, Delete |
| Extras | List, Create, Get, Update, Delete |
| Coupons | List, Create, Get, Update, Delete |
| Pricing rules PRO | List, Create, Get, Update, Delete, Pricing calendar |
| Tax rates PRO | List, Create, Get, Update, Delete |
| Reports PRO | Rentals |
Public routes, for the booking form, the customer booking page or your own front end:
| Resource | Endpoints |
|---|---|
| Booking form | Options, Search cars, Hold a car, Get held booking, Apply coupon, Check out, Release hold |
| Customer portal | View booking, Cancel booking, Find booking |
A booking front end calls them in this order: Options once, Search cars for the dates, Hold a car for the chosen model, which returns a token, then Apply coupon if the visitor has one and Check out with the token.
Public routes
The public routes run without a WordPress login and need no nonce. A public route cannot ask who is calling, so it checks three things, cheapest first.
- Honeypot. Hold a car, Check out and Find booking refuse a request whose hidden
websitefield is filled in, with400plugixa_car_rental_rejected. - Rate limit. Counted per visitor address and per route, see below.
- CAPTCHA. Nothing is bundled. A site adds one with the
plugixa_car_rental_public_request_captchafilter, which receives the request’scaptcha_token; a refusal answers403plugixa_car_rental_captcha_failed.
A held booking is opened by the token Hold a car returns, and a customer’s booking by the manage link in their emails. Both are long random values, and nothing else opens a booking from the public side. Public responses carry Cache-Control: no-store, and are not passed through the plugixa_car_rental_filter_output filter.
Rate limits
| Route | Requests per hour |
|---|---|
GET /public/options |
300 |
POST /public/search |
120 |
POST /public/holds |
15 |
POST /public/release |
30 |
POST /public/booking |
120 |
POST /public/coupon |
20 |
POST /public/checkout |
15 |
POST /public/manage |
60 |
POST /public/manage/cancel |
10 |
POST /public/manage/find |
5 |
Over the limit the answer is 429 plugixa_car_rental_rate_limited, with no Retry-After header. The staff routes have no rate limit.
The counter is kept per address, and the address is read from REMOTE_ADDR only. On a site behind a proxy or a CDN every visitor shares the proxy’s address and therefore one counter: use the plugixa_car_rental_client_ip filter to read the header your proxy sets. See Hooks.
Routes not for you to call
/public/payment/return, where a payment provider sends the customer’s browser back, and /public/payment/webhook/{gateway}, where a provider reports a payment, are for the providers. With Pro, /public/manage/invoice and /public/manage/contract serve the customer’s PDFs from their manage link, and /calendar/...ics serves the calendar feeds set up under Calendar sync.
Errors
Errors use the standard WordPress shape:
{
"code": "plugixa_car_rental_forbidden",
"message": "You are not allowed to do that.",
"data": { "status": 403 }
}
These can come back from any endpoint. Each endpoint page lists its own errors on top of them.
| Status | Code | When |
|---|---|---|
401 |
plugixa_car_rental_forbidden |
A staff route was called without a signed-in user: no credentials, or a cookie without its nonce. |
401 |
incorrect_password |
WordPress refused the Application Password before the plugin saw the request. |
403 |
plugixa_car_rental_forbidden |
The user is signed in but lacks the capability. |
404 |
plugixa_car_rental_not_found |
No record with that id, or it is in the trash. |
409 |
plugixa_car_rental_conflict |
The modified_at you sent is not the stored one. See Edit conflicts. |
400 |
plugixa_car_rental_unknown_filter |
A list filter names a field the list does not have. |
400 |
plugixa_car_rental_unknown_operator |
A list filter uses an operator that does not work on that field. |
400 |
plugixa_car_rental_missing_value |
A list filter has fewer values than its operator needs. |
400 |
plugixa_car_rental_invalid_date |
A list filter on a date field has a value that is not a date. |
400 |
rest_invalid_param |
A list parameter failed validation, for example per_page above 200 or order other than asc or desc. |
422 |
plugixa_car_rental_invalid_choice, plugixa_car_rental_not_a_number, plugixa_car_rental_number_too_large, plugixa_car_rental_negative_number, plugixa_car_rental_too_long |
A body field failed validation. data.field names the field; data.allowed lists the accepted values of a choice, and data.max the longest text allowed. Numbers are capped at 99999999999999. |
429 |
plugixa_car_rental_rate_limited |
A public route’s rate limit was reached. |
404 |
rest_no_route |
No such route or method, an app that is switched off, or a Pro route on a site without Pro. |
plugixa_car_rental_too_long has two meanings: a text longer than its column (with data.max), and on a booking, a rental longer than the booking rules allow (with data.field set to return_at).
Filtering what comes back
| Hook | Changes |
|---|---|
plugixa_car_rental_filter_output |
Every successful staff payload, with the kind of record, before it is sent. The place to hide fields from some users |
plugixa_car_rental_user_can |
The verdict of every capability check |
plugixa_car_rental_public_options |
What Booking Form Options returns |
plugixa_car_rental_portal_view |
What View Booking returns |
plugixa_car_rental_client_ip |
The address a public request is counted against |
plugixa_car_rental_public_request_captcha |
Adds a CAPTCHA check to the public routes |
plugixa_car_rental_public_request_refusal |
Refuses a public search or hold before any car is looked at |
Register your own routes on the plugixa_car_rental_register_rest_routes action, which passes the namespace (and the plugin’s service container as a second argument):
add_action( 'plugixa_car_rental_register_rest_routes', function ( $namespace ) {
register_rest_route(
$namespace,
'/fleet-status',
array(
'methods' => 'GET',
'callback' => fn() => rest_ensure_response( array( 'open' => true ) ),
'permission_callback' => fn() => current_user_can( 'plugixa_car_rental_view' ),
)
);
} );
See Hooks for the full set, including the actions that fire when a booking is created, changes status or is paid.
Troubleshooting
| Symptom | Usual cause |
|---|---|
401 from a script |
The Application Password is wrong or missing, or the host strips the Authorization header before PHP sees it. |
401 or 403 from the browser |
The REST nonce is missing or stale. Reload the admin. |
403 with a valid login |
The user lacks the capability. Grant it with a role editor, or use an administrator account. |
404 rest_no_route |
The app is switched off in the App manager, or the route is Pro and Pro is not active. |
422 plugixa_car_rental_invalid_datetime on a booking |
A bare date, or a time with an offset. Send local time at the location, YYYY-MM-DD HH:MM. |
422 plugixa_car_rental_not_priced |
No published rate plan covers that rental length for the model. Add one under Rates. |
429 on a public route behind a proxy |
Every visitor shares the proxy’s address. Set plugixa_car_rental_client_ip. |
409 on an update |
Somebody saved the record after you read it. Read it again and reapply your change. |
See also
- Change behaviour in PHP instead: Hooks.
- The screens behind these records: Bookings, Fleet, Locations, Rates.
- How the public flow looks to a customer: Booking form and Customer portal.
Quick Links
Share