View Categories

Adding a Rental to the Cart & Creating Orders via the API

6 min read

There are two ways to book a rental for a customer through the API, and they suit different jobs. A headless storefront or mobile app adds the rental to a WooCommerce cart with the public Store API and lets WooCommerce run the checkout, payment and emails: the customer pays, and the reservation is created by the same code as a normal checkout. A back-office system (a point of sale, a CRM, a migration) creates the order directly with WooCommerce’s orders API and the rental is booked from the order’s line-item meta. Both are described below; the conventions are in the REST API overview.

Adding a rental to the cart (Store API) #

WooCommerce’s Store API lives at /wp-json/wc/store/v1/, needs no API key, and identifies the cart with a Cart-Token header instead of a cookie. Bookings & Tours extends its cart/add-item endpoint with the rental fields, so the request carries the same values the product page’s calendar posts, and goes through the same validation: the start date cannot be in the past or too close, neither date can fall on a closed day, and the requested quantity must be free for the whole period including turnover buffers.

FieldValue
idThe product id (or variation id) — a WooCommerce field.
quantityHow many units — a WooCommerce field.
sibooking_fromStart date, Y-m-d. Required for a rental.
sibooking_toEnd date, Y-m-d. Required for a rental (send the same day as the start for a one-day hire).
sibooking_from_timeStart time, HH:mm. Only for products rented by date and time.
sibooking_to_timeEnd time, HH:mm. Only for products rented by date and time.
isbookingpurchase1 to buy a rentable product outright instead of renting it (for products that offer both). No dates are needed then.

The hyphenated names the product form uses (sibooking-from, sibooking-to, …) are accepted as well. A fixed-length product still needs both dates: send the end date the plugin’s calendar would have chosen (start plus the fixed length).

Step by step #

1. Get a cart token. Any Store API cart request returns a Cart-Token response header; keep it and send it back on every following request. It is valid for 48 hours and identifies this customer’s cart.

curl -i https://your-store.com/wp-json/wc/store/v1/cart
# … Cart-Token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9…

2. Check availability first. Before offering the dates, ask the rental API whether enough units are free for them. This needs your API key, so make the call from your server (or your app’s backend), never from the browser. The answer uses the same buffers and closed-day rules as the cart, so what it accepts the cart will accept too.

curl -u "ck_your_key:cs_your_secret" \
  "https://your-store.com/wp-json/wc-sibooking/v1/availability?product_id=123&start=2026-10-03&end=2026-10-05&quantity=1"

{ "product_id": 123, "max_quantity": 3, "booked": 1, "available": 2, "requested": 1, "ok": true }

Only continue when ok is true. To grey out unavailable days in your own date picker before the customer picks anything, load GET /wc-sibooking/v1/availability/calendar?product_id=123 once (also from the server) and use its fully_booked_days. See the availability endpoints.

3. Add the rental.

curl -X POST https://your-store.com/wp-json/wc/store/v1/cart/add-item \
  -H "Content-Type: application/json" \
  -H "Cart-Token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9…" \
  -d '{
    "id": 123,
    "quantity": 1,
    "sibooking_from": "2026-10-03",
    "sibooking_to": "2026-10-05"
  }'

The response (status 201) is the whole cart. The rental line shows the dates under item_data, and totals already reflects the rental price for those dates and quantity, calculated by the product’s pricing rules:

{
  "items": [
    {
      "key": "a4df80da711d84c874fcb1d7dfb7260c",
      "id": 123,
      "name": "Canon EOS R6 body",
      "quantity": 1,
      "item_data": [
        { "key": "Start Date", "value": "October 3, 2026" },
        { "key": "End Date", "value": "October 5, 2026" }
      ],
      "totals": { "line_total": "7500", "currency_minor_unit": 2 }
    }
  ],
  "items_count": 1,
  "totals": { "total_items": "7500", "total_price": "7500", "currency_code": "USD", "currency_minor_unit": 2 }
}

Amounts in the Store API are strings in the currency’s minor unit (cents), as for every WooCommerce cart. When the rental cannot be added, the answer is a 400 with the same message the customer would see on the product page:

{ "code": "woocommerce_rest_add_to_cart_error", "message": "The start or end date is disabled", "data": { "status": 400 } }
{ "code": "woocommerce_rest_add_to_cart_error", "message": "Not enough inventory for the dates selected", "data": { "status": 400 } }
{ "code": "woocommerce_rest_add_to_cart_error", "message": "Please enter the booking dates", "data": { "status": 400 } }

A refusal here means the calendar changed between the availability check and the add: another customer took the last unit, or the dates crossed a closed day. Show the message and let the customer pick again.

4. Check out. The Store API checkout takes the addresses and the payment method; card gateways that support the block checkout accept their payment_data here as documented by WooCommerce.

curl -X POST https://your-store.com/wp-json/wc/store/v1/checkout \
  -H "Content-Type: application/json" \
  -H "Cart-Token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9…" \
  -d '{
    "billing_address":  { "first_name": "Jane", "last_name": "Smith", "address_1": "1 High Street", "city": "Leeds", "postcode": "LS1 1AA", "country": "GB", "email": "[email protected]" },
    "shipping_address": { "first_name": "Jane", "last_name": "Smith", "address_1": "1 High Street", "city": "Leeds", "postcode": "LS1 1AA", "country": "GB" },
    "payment_method": "bacs"
  }'

When the order is placed the plugin creates the reservation from the order (the same woocommerce_store_api_checkout_order_processed path the block checkout uses), sends the usual emails, and the booking is visible at GET /wc-sibooking/v1/bookings?order_id=….

The same flow in JavaScript #

const store = 'https://your-store.com/wp-json/wc/store/v1';
let cartToken = '';

async function storeApi(path, options = {}) {
  const headers = { 'Content-Type': 'application/json', ...(cartToken ? { 'Cart-Token': cartToken } : {}) };
  const res = await fetch(store + path, { ...options, headers });
  cartToken = res.headers.get('Cart-Token') || cartToken;   // keep the cart between calls
  const body = await res.json();
  if (!res.ok) throw new Error(body.message);              // "The start or end date is disabled", …
  return body;
}

await storeApi('/cart');                                     // 1. obtain the token
// 2. (on your server) GET /wc-sibooking/v1/availability?product_id=123&start=…&end=… and only go on when ok === true
const cart = await storeApi('/cart/add-item', {              // 3. add the rental
  method: 'POST',
  body: JSON.stringify({ id: 123, quantity: 1, sibooking_from: '2026-10-03', sibooking_to: '2026-10-05' })
});
console.log(cart.totals.total_price);                        // "7500"
const order = await storeApi('/checkout', {                  // 4. place the order
  method: 'POST',
  body: JSON.stringify({ billing_address: {…}, shipping_address: {…}, payment_method: 'bacs' })
});
console.log(order.order_id, order.status);

From a page that is part of the WordPress site itself (a theme or a block), send the Nonce header WooCommerce prints as wcSettings.storeApiNonce (or the wc_store_api nonce) instead of a Cart-Token, and the customer’s normal session cart is used.

Creating an order with a rental (wc/v3) #

For orders taken outside the storefront, create them with WooCommerce’s POST /wp-json/wc/v3/orders (API key required) and put the rental dates on the line item as meta. Bookings & Tours books every line item that carries sibooking-from and sibooking-to, exactly as it does for a checkout, and later updates to the same line item’s dates through PUT /wc/v3/orders/{id} move the reservation rather than adding a second one.

curl -u "ck_your_key:cs_your_secret" -X POST \
  -H "Content-Type: application/json" \
  https://your-store.com/wp-json/wc/v3/orders \
  -d '{
    "payment_method": "bacs",
    "set_paid": false,
    "billing": { "first_name": "Jane", "last_name": "Smith", "email": "[email protected]" },
    "line_items": [
      {
        "product_id": 123,
        "quantity": 1,
        "meta_data": [
          { "key": "sibooking-from", "value": "2026-10-03 00:00:00" },
          { "key": "sibooking-to",   "value": "2026-10-05 23:59:00" }
        ]
      }
    ]
  }'
  • The meta values are full datetimes. Use 00:00:00 for the start and 23:59:00 for the end of a date-only rental; for a timed rental use the real times.
  • The reservation takes the order’s status into account: a cancelled, refunded or failed order releases its units, and putting it back to processing or completed reserves them again.
  • Unlike the cart, this path does not refuse an overbooking (WooCommerce’s orders API has no validation hook for line items). Call GET /wc-sibooking/v1/availability first and only create the order when ok is true.
  • The line’s price is what you send in total/subtotal (or the product’s regular price if you send nothing). To charge the rental price for the dates, read it from the product’s pricing rules on your side, or add the item to a cart with the Store API and take the total from there.

Which one to use #

Store API cartwc/v3 orders
Who calls itThe customer’s browser or appYour server
AuthenticationNone (Cart-Token)API key
Dates validated against the calendarYes, same rules as the product pageNo, check /availability first
Rental priceCalculated by the pluginSent by you
PaymentTaken at checkout by the gatewayRecorded by you (set_paid, transaction id)
EmailsNormal order emailsNormal order emails