---
title: Take a hire booking
description: Book studio-owned stock for a date window, and understand how availability is judged.
icon: package
---

A hire line rents something you own for a window of dates, rather than buying
stems for an event. It goes through the same endpoint as any other line, with
`mode: "hire"`.

## What can be hired

Only a variant marked `hireable`, which the app allows on material products
only. A hireable variant carries an `inventoryQty`: how many units the studio
owns. Setting it is a catalogue write:

```bash
curl -X POST "https://app.bloomcount.com/api/v1/products" \
  -H "Authorization: Bearer bloomcount_..." \
  -H "Content-Type: application/json" \
  -d '{
    "items": [
      {
        "productId": "prod_...",
        "variantId": "var_...",
        "variant": { "hireable": true, "inventoryQty": 24 }
      }
    ]
  }'
```

Daily fees are not set here. They live on a price list whose `productType` is
`hire`, and the event points at one through `hirePriceListId`.

## Booking

```bash
curl -X POST "https://app.bloomcount.com/api/v1/events/evt_.../items" \
  -H "Authorization: Bearer bloomcount_..." \
  -H "Content-Type: application/json" \
  -d '{
    "items": [
      {
        "variantId": "var_...",
        "quantity": 10,
        "groupId": "evtgrp_...",
        "mode": "hire",
        "hireStart": 1781424000000,
        "hireEnd": 1781596800000
      }
    ]
  }'
```

Both dates are required and are milliseconds since the epoch. A hire line takes
no `supplierId`: the stock is yours, so there is nobody to buy it from, and
sending one fails the row with `supplier_not_allowed_on_hire`.

On the way back, a hire line carries `hireDays` and `dailyFeeExclTax`, the fee
snapshotted from the event's hire price list when the line was written.

## How availability is judged

When a hire line is created, or its quantity or dates change, the API counts
what is already committed for that variant and refuses to oversell.

1. **Start from what you own**

    The variant's `inventoryQty`. A variant with none available has nothing to
    hire, and every booking fails.

2. **Subtract overlapping bookings**

    Every other hire line for the same variant whose window touches yours, on any
    event. Two bookings overlap unless one ends before the other starts.

3. **Ignore cancelled events**

    Lines on an event whose status is `cancelled` are not held, so cancelling
    frees the stock immediately.

4. **Count the group multiplier**

    A line is counted at its effective quantity, not its written one. Ten of
    something inside a group with `quantityMode: "group"` and `groupQuantity: 4`
    holds forty units, not ten.

When you edit an existing line, that line is excluded from the count, so
changing ten to twelve is judged against the other bookings rather than against
itself.

A row asking for more than is free fails with the number that is actually
available:

```json
{
  "results": [],
  "errors": [{ "index": 0, "error": "hire_unavailable:6" }]
}
```

Six units are free across those dates. The rest of the batch still lands, so a
single unavailable row does not cost you the others.

:::warning[Availability is judged at write time]
Nothing reserves stock between reading and writing. If two integrations book
the same window at once, the second gets `hire_unavailable`. Treat that as a
normal outcome and surface it, rather than as a failure to retry blindly.
:::

## Changing or cancelling

Same endpoint, with the line's id. Changing dates re-checks availability
against the new window.

```json
{
  "items": [
    { "itemId": "evti_...", "quantity": 8 },
    { "itemId": "evti_...", "hireStart": 1781424000000, "hireEnd": 1781683200000 },
    { "itemId": "evti_...", "remove": true }
  ]
}
```

Sending hire dates for a line that is not a hire line fails with
`not_a_hire_line`, and booking a variant that is not marked hireable fails with
`variant_not_hireable`.
