---
title: "Bookings"
description: "Create booking requests and administer bookings with the Requiply SDK."
canonical_url: "https://requiply.com/docs/en/developers/typescript-sdk/bookings"
---
# Bookings

> Create booking requests and administer bookings with the Requiply SDK.

## `bookings.create()`

```ts
bookings.create(
  input: BookingCreateInput,
  options?: RequiplyRequestOptions,
): Promise<BookingCreateResponse>
```

Required: `startDate`, `endDate`, `items` (up to 100), and `deliveryType` (`pickup` or `delivery`). A regular booking also needs `paymentType`; a booking request (`asRequest: true`) does not. Optional: `bundles`, `services`, `clientId` or inline `client` details, `allowOutsideWorkingHours`, `adjustment`, and `saleLines`.

`paymentType` accepts `payOnPickup`, `stripe`, `wayforpay`, `monobank`, or `liqpay`. An online method needs an active integration for that provider.

```ts
const result = await requiply.bookings.create({
  startDate: '2026-07-01T10:00:00',
  endDate: '2026-07-03T18:00:00',
  items: [{ groupId: cameraKitId, quantity: 1 }],
  deliveryType: 'pickup',
  asRequest: true,
  client: { name: 'Alex', email: 'customer@example.com' },
});
```

`client` needs an `email` or a `phone`, and may add `name` and `lastName`. It is matched to an existing client by email, then by phone, and created when neither matches. A server integration that already knows the client can pass `clientId` instead. A booking request does not hold inventory and sets up no payment until staff approve it.

<warning>

Publishable keys may use this method only for booking requests with `asRequest: true`. Use a secret key on a trusted server for regular booking creation.

</warning>

The status the booking starts in comes from the organization's [Booking creation settings](https://requiply.com/docs/en/guides/settings/bookings/booking-creation#choosing-the-starting-statuses): **Status for booking requests** when `asRequest` is `true`, **Default status for new bookings** otherwise. Statuses belong to each organization and can be renamed, so do not hard-code a status name such as `Request` or `New` in your integration.

A date-time without an offset, such as `2026-07-01T10:00:00`, is read as local time in the organization's timezone, daylight saving included. A value with `Z` or an offset is used as given. A date with no time, such as `2026-07-01`, is read as midnight and usually fails the working-hours check, so always send a time.

`startDate` and `endDate` must each fall on a working day inside the organization's working hours, evaluated in the organization timezone. Otherwise the response has `success: false`, `reason: "working_hours"`, and a message describing the allowed window. A secret key may pass `allowOutsideWorkingHours: true` to skip that check for a pickup or return the business has agreed to handle outside its schedule. Publishable keys cannot use the flag and receive a `403`.

### Booking cost adjustment

The REST endpoint `POST /api/public/v1/bookings/create` also accepts one [booking cost adjustment](https://requiply.com/docs/en/guides/bookings/changing/cost-adjustment), a one-off discount or surcharge on this booking:

```json
"adjustment": { "type": "percent", "value": -10, "reason": "Returning client" }
```

`type` is `units` (charged days or months), `percent` (−100 to 100), or `amount` (organization currency). The value is signed: negative discounts, positive surcharges. Only a **secret key** may send it, the key's permission group needs **Allow booking cost adjustments**, and the type must be enabled under the organization's [Features](https://requiply.com/docs/en/guides/settings/organization/features#enable-booking-cost-adjustments). Otherwise the response is a `403` (for example *adjustment requires a secret key* or *Booking adjustments are not enabled for this organization*), or a `400` for a zero value without a reason. The same field works on [`pricing.quote()`](https://requiply.com/docs/en/developers/typescript-sdk/availability-and-pricing#pricingquote), so you can show the adjusted price first.

### Bundles

Since 0.2.1, `bundles: [{ bundleId, quantity }]` books whole sets of a [bundle](https://requiply.com/docs/en/guides/inventory/bundles), with the IDs from [`bundles.list()`](https://requiply.com/docs/en/developers/typescript-sdk/inventory-and-categories#bundleslist). `items` may then be empty. The bundle's products are written to the booking as ordinary item lines with the bundle discount applied, and the client discount stacks on top. Sending bundles while the organization has the Bundles feature off answers `403`. Quote the same `bundles` with [`pricing.quote()`](https://requiply.com/docs/en/developers/typescript-sdk/availability-and-pricing#pricingquote) first to show `bundleDiscountAmount`.

### Goods for sale

With the Sales feature on, `saleLines: [{ saleItemId, quantity }]` sells goods together with the booking, at their catalog price. A publishable key can add them to a booking request only while the organization [sells to customers](https://requiply.com/docs/en/developers/typescript-sdk/sales#with-a-publishable-key), and never beyond the stock on hand. The goods total is added to `totalCost`, the stock drops when the booking is created, and it comes back if the booking is cancelled. See [Sales and Goods for Sale](https://requiply.com/docs/en/developers/typescript-sdk/sales#goods-on-a-booking).

Publishable booking requests cannot include supplier-sourced subrental product IDs. Secret-key booking creation can include them when the organization has enabled Subrentals; each selected product creates a snapshotted procurement line instead of assigning an owned inventory unit.

### Handling the response

`bookings.create()` returns one of two shapes, told apart by `success`. A booking the organization cannot accept does **not** throw: it resolves with `success: false`. TypeScript only lets you read `reservationId` after checking `success`.

```ts
if (result.success) {
  console.log('Booking', result.reservationOrgId, result.isRequest);
  if (result.paymentUrl) redirectTo(result.paymentUrl);
} else {
  console.log(result.message, result.reason);
}
```

<table>
<thead>
  <tr>
    <th>
      <code>
        success: true
      </code>
      
       (<code>
        BookingCreated
      </code>
      
      )
    </th>
    
    <th>
      <code>
        success: false
      </code>
      
       (<code>
        BookingRefused
      </code>
      
      )
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        reservationId
      </code>
      
      : internal ID, for <code>
        bookings.get()
      </code>
      
      .
    </td>
    
    <td>
      <code>
        message
      </code>
      
      : why the booking was refused, for example the items are not available for those dates.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        reservationOrgId
      </code>
      
      : the booking number staff see in the app.
    </td>
    
    <td>
      <code>
        reason
      </code>
      
      : <code>
        working_hours
      </code>
      
       or <code>
        minimum_duration
      </code>
      
       when a booking rule was broken; absent for other refusals such as availability.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        isRequest
      </code>
      
      : <code>
        true
      </code>
      
       for a booking request.
    </td>
    
    <td>
      <code>
        minimumDuration
      </code>
      
      : details of a <code>
        minimum_duration
      </code>
      
       refusal, see below.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        paymentUrl
      </code>
      
       or <code>
        paymentRedirect
      </code>
      
      : where to send the customer for an online payment. A <code>
        post_form
      </code>
      
       redirect includes form <code>
        fields
      </code>
      
      .
    </td>
    
    <td>
      
    </td>
  </tr>
</tbody>
</table>

Both shapes include `totalCost` and `currency`. Invalid input, such as a missing field or an unknown product, still throws a [`RequiplyError`](https://requiply.com/docs/en/developers/typescript-sdk/error-handling).

### Minimum booking duration

`endDate` must be after `startDate` (otherwise `400` *endDate must be after startDate*), and the booking must be at least the **Minimum booking duration** from the organization's [Booking duration](https://requiply.com/docs/en/guides/settings/bookings/booking-duration) settings (24 hours unless changed). A shorter booking is refused with `success: false`, `reason: "minimum_duration"`, and a `minimumDuration` object you can use to offer a valid return:

```json
{
  "success": false,
  "reason": "minimum_duration",
  "message": "...",
  "minimumDuration": {
    "minimumDurationMinutes": 1440,
    "requestedDurationMinutes": 240,
    "earliestReturn": "2026-10-07T10:00",
    "timeZone": "Europe/Kyiv"
  }
}
```

`earliestReturn` is the earliest return accepted for that pickup, as local time (`YYYY-MM-DDTHH:mm`) in `timeZone`. Durations are measured on the organization's local clock, so a daylight-saving change does not make a booking shorter or longer.

## `bookings.list()`

```ts
bookings.list(
  params?: BookingListParams,
  options?: RequiplyRequestOptions,
): Promise<BookingListResponse>
```

This is a secret-key server operation. Bookings come newest first, by creation time. Optional filters:

<table>
<thead>
  <tr>
    <th>
      Parameter
    </th>
    
    <th>
      Effect
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        dateFrom
      </code>
      
      , <code>
        dateTo
      </code>
    </td>
    
    <td>
      Bookings whose <strong>
        pickup
      </strong>
      
       (<code>
        startDate
      </code>
      
      ) falls in the range. A bare date is midnight, so <code>
        dateTo: '2026-07-31'
      </code>
      
       leaves out pickups later on 31 July; pass <code>
        '2026-07-31T23:59:59'
      </code>
      
       or the next day.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        createdFrom
      </code>
      
      , <code>
        createdTo
      </code>
    </td>
    
    <td>
      Bookings created in the range. Useful for syncing new bookings since the last run.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        status
      </code>
    </td>
    
    <td>
      One status name, or several separated by commas, case-insensitive. Statuses belong to each organization, so read the names from your own app. An unknown name returns an empty list.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        clientId
      </code>
    </td>
    
    <td>
      One client's bookings.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        limit
      </code>
      
      , <code>
        cursor
      </code>
    </td>
    
    <td>
      Page size (1 to 200, default 50) and the <code>
        nextCursor
      </code>
      
       of the previous page.
    </td>
  </tr>
</tbody>
</table>

```ts
const page = await requiply.bookings.list({
  dateFrom: '2026-07-01',
  dateTo: '2026-07-31',
  limit: 25,
});
```

The response includes `bookings`, `currency`, and `nextCursor: string | null`. Status, client, total-cost, and organization reservation fields on list items can be `null`.

## `bookings.get()`

```ts
bookings.get(
  id: number,
  params?: { idKind?: 'internal' | 'org' },
  options?: RequiplyRequestOptions,
): Promise<BookingDetailResponse>
```

Use `idKind: 'org'` when `id` is the organization-facing reservation number. Otherwise omit it or use `internal`.

```ts
const { booking } = await requiply.bookings.get(1042, { idKind: 'org' });
```

The booking includes items, services, and payments. Several status, client, price, provider, and payment URL fields can be `null`. `items` lists the booked quantity of each product, even when units are only assigned at pickup.

`saleItems` lists goods sold with the booking and `saleId` points to their [sale](https://requiply.com/docs/en/developers/typescript-sdk/sales#salesget). Both are empty or `null` when nothing was sold or the Sales feature is off. The goods total is always inside `totalCost`.

## `bookings.addNote()`

```ts
bookings.addNote(
  id: number,
  content: string,
  options?: RequiplyRequestOptions,
): Promise<BookingNoteResponse>
```

```ts
const note = await requiply.bookings.addNote(42, 'Customer confirmed pickup time.');
```

`content` is up to 4000 characters. The note is internal: staff see it on the booking, the client never does. The response contains `noteId`, `reservationId`, and nullable `createdAt`. This is a secret-key server operation.


## Sitemap

See the full [sitemap](https://requiply.com/docs/sitemap.md) for all pages.
