---
title: "Reservas"
description: "Crea solicitudes de reserva y administra las reservas con el SDK de Requiply."
canonical_url: "https://requiply.com/docs/es/developers/typescript-sdk/bookings"
---
# Reservas

> Crea solicitudes de reserva y administra las reservas con el SDK de Requiply.

## `bookings.create()`

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

Obligatorios: `startDate`, `endDate`, `items` (hasta 100) y `deliveryType` (`pickup` o `delivery`). Una reserva normal también necesita `paymentType`; una solicitud de reserva (`asRequest: true`), no. Opcionales: `bundles`, `services`, `clientId` o los datos del cliente en `client`, `allowOutsideWorkingHours`, `adjustment` y `saleLines`.

`paymentType` acepta `payOnPickup`, `stripe`, `wayforpay`, `monobank` o `liqpay`. Un método online necesita una integración activa para esa pasarela.

```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` necesita un `email` o un `phone`, y puede añadir `name` y `lastName`. Se busca un cliente existente por email y, después, por teléfono, y se crea uno nuevo cuando no coincide ninguno. Una integración de servidor que ya conoce al cliente puede pasar `clientId` en su lugar. Una solicitud de reserva no bloquea inventario ni prepara ningún pago hasta que el personal la aprueba.

<warning>

Las claves publicables solo pueden usar este método para solicitudes de reserva con `asRequest: true`. Usa una clave secreta en un servidor de confianza para crear reservas normales.

</warning>

El estado inicial de la reserva viene de los [ajustes de creación de reservas](https://requiply.com/docs/es/guides/settings/bookings/booking-creation#elegir-los-estados-iniciales) de la organización: **Status for booking requests** (estado de las solicitudes de reserva) cuando `asRequest` es `true` y **Default status for new bookings** (estado por defecto de las reservas nuevas) en caso contrario. Los estados son de cada organización y se pueden renombrar, así que no escribas en tu integración un nombre de estado fijo como `Request` o `New`.

Una fecha y hora sin desfase horario, como `2026-07-01T10:00:00`, se interpreta como hora local en la zona horaria de la organización, con el horario de verano incluido. Un valor con `Z` o con desfase se usa tal cual. Una fecha sin hora, como `2026-07-01`, se interpreta como medianoche y normalmente no pasa la comprobación del horario, así que envía siempre una hora.

Tanto `startDate` como `endDate` deben caer en un día laborable dentro del horario de la organización, evaluado en su zona horaria. Si no, la respuesta tiene `success: false`, `reason: "working_hours"` y un mensaje que describe la franja permitida. Una clave secreta puede pasar `allowOutsideWorkingHours: true` para saltarse esa comprobación en una recogida o una devolución que el negocio ha aceptado atender fuera de su horario. Las claves publicables no pueden usar esta opción y reciben un `403`.

### Ajuste del coste de la reserva

El endpoint REST `POST /api/public/v1/bookings/create` también acepta un [ajuste del coste de la reserva](https://requiply.com/docs/es/guides/bookings/changing/cost-adjustment), un descuento o un recargo puntual en esta reserva:

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

`type` es `units` (días o meses cobrados), `percent` (de −100 a 100) o `amount` (en la moneda de la organización). El valor lleva signo: negativo para descuentos, positivo para recargos. Solo lo puede enviar una **clave secreta**, el grupo de permisos de la clave necesita **Allow booking cost adjustments** (permitir ajustes del coste de la reserva) y el tipo tiene que estar activado en las [funciones](https://requiply.com/docs/es/guides/settings/organization/features#activar-los-ajustes-del-coste-de-la-reserva) de la organización. Si no, la respuesta es un `403` (por ejemplo *adjustment requires a secret key* o *Booking adjustments are not enabled for this organization*), o un `400` si el valor es cero y no hay motivo. El mismo campo funciona en [`pricing.quote()`](https://requiply.com/docs/es/developers/typescript-sdk/availability-and-pricing#pricingquote), así que puedes mostrar antes el precio ajustado.

### Packs

Desde la 0.2.1, `bundles: [{ bundleId, quantity }]` reserva packs completos de un [pack](https://requiply.com/docs/es/guides/inventory/bundles), con los ID de [`bundles.list()`](https://requiply.com/docs/es/developers/typescript-sdk/inventory-and-categories#bundleslist). En ese caso, `items` puede estar vacío. Los productos del pack se guardan en la reserva como líneas de artículo normales con el descuento del pack aplicado, y el descuento del cliente se suma encima. Enviar packs cuando la organización tiene desactivada la función de packs responde `403`. Calcula antes un presupuesto con los mismos `bundles` en [`pricing.quote()`](https://requiply.com/docs/es/developers/typescript-sdk/availability-and-pricing#pricingquote) para mostrar `bundleDiscountAmount`.

### Productos en venta

Con la función de ventas activada, `saleLines: [{ saleItemId, quantity }]` vende productos junto con la reserva, a su precio de catálogo. Una clave publicable solo puede añadirlos a una solicitud de reserva mientras la organización [vende a los clientes](https://requiply.com/docs/es/developers/typescript-sdk/sales#con-una-clave-publicable), y nunca por encima del stock disponible. El total de los productos se suma a `totalCost`, el stock baja al crear la reserva y se recupera si la reserva se cancela. Consulta [Ventas y productos en venta](https://requiply.com/docs/es/developers/typescript-sdk/sales#productos-en-una-reserva).

Las solicitudes de reserva con clave publicable no pueden incluir ID de productos de subalquiler de proveedores. La creación de reservas con clave secreta sí puede incluirlos cuando la organización ha activado los subalquileres; cada producto seleccionado crea una línea de aprovisionamiento con una copia fija de sus datos en lugar de asignar una unidad del inventario propio.

### Gestionar la respuesta

`bookings.create()` devuelve una de dos estructuras, que se distinguen por `success`. Una reserva que la organización no puede aceptar **no** lanza una excepción: se resuelve con `success: false`. TypeScript solo te deja leer `reservationId` después de comprobar `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>
      
      : el ID interno, para <code>
        bookings.get()
      </code>
      
      .
    </td>
    
    <td>
      <code>
        message
      </code>
      
      : por qué se ha rechazado la reserva, por ejemplo porque los artículos no están disponibles en esas fechas.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        reservationOrgId
      </code>
      
      : el número de reserva que ve el personal en la app.
    </td>
    
    <td>
      <code>
        reason
      </code>
      
      : <code>
        working_hours
      </code>
      
       o <code>
        minimum_duration
      </code>
      
       cuando no se ha cumplido una regla de reserva; no aparece en otros rechazos, como los de disponibilidad.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        isRequest
      </code>
      
      : <code>
        true
      </code>
      
       en una solicitud de reserva.
    </td>
    
    <td>
      <code>
        minimumDuration
      </code>
      
      : los detalles de un rechazo <code>
        minimum_duration
      </code>
      
       (más abajo).
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        paymentUrl
      </code>
      
       o <code>
        paymentRedirect
      </code>
      
      : adónde enviar al cliente para un pago online. Una redirección <code>
        post_form
      </code>
      
       incluye los <code>
        fields
      </code>
      
       del formulario.
    </td>
    
    <td>
      
    </td>
  </tr>
</tbody>
</table>

Las dos estructuras incluyen `totalCost` y `currency`. Los datos de entrada no válidos, como un campo que falta o un producto desconocido, siguen lanzando un [`RequiplyError`](https://requiply.com/docs/es/developers/typescript-sdk/error-handling).

### Duración mínima de la reserva

`endDate` debe ser posterior a `startDate` (si no, `400` *endDate must be after startDate*), y la reserva debe durar al menos la **Minimum booking duration** (duración mínima de la reserva) definida en los ajustes de [duración de la reserva](https://requiply.com/docs/es/guides/settings/bookings/booking-duration) de la organización (24 horas salvo que se haya cambiado). Una reserva más corta se rechaza con `success: false`, `reason: "minimum_duration"` y un objeto `minimumDuration` que puedes usar para ofrecer una devolución válida:

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

`earliestReturn` es la devolución más temprana que se acepta para esa recogida, como hora local (`YYYY-MM-DDTHH:mm`) en `timeZone`. Las duraciones se miden con el reloj local de la organización, así que un cambio de horario de verano no hace que una reserva sea más corta ni más larga.

## `bookings.list()`

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

Es una operación de servidor con clave secreta. Las reservas salen de la más reciente a la más antigua, por fecha de creación. Filtros opcionales:

<table>
<thead>
  <tr>
    <th>
      Parámetro
    </th>
    
    <th>
      Efecto
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        dateFrom
      </code>
      
      , <code>
        dateTo
      </code>
    </td>
    
    <td>
      Las reservas cuya <strong>
        recogida
      </strong>
      
       (<code>
        startDate
      </code>
      
      ) cae en el intervalo. Una fecha sola es medianoche, así que <code>
        dateTo: '2026-07-31'
      </code>
      
       deja fuera las recogidas posteriores del 31 de julio; pasa <code>
        '2026-07-31T23:59:59'
      </code>
      
       o el día siguiente.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        createdFrom
      </code>
      
      , <code>
        createdTo
      </code>
    </td>
    
    <td>
      Las reservas creadas en el intervalo. Útil para sincronizar las reservas nuevas desde la última ejecución.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        status
      </code>
    </td>
    
    <td>
      Un nombre de estado, o varios separados por comas, sin distinguir mayúsculas de minúsculas. Los estados son de cada organización, así que toma los nombres de tu propia app. Un nombre desconocido devuelve una lista vacía.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        clientId
      </code>
    </td>
    
    <td>
      Las reservas de un cliente.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        limit
      </code>
      
      , <code>
        cursor
      </code>
    </td>
    
    <td>
      El tamaño de página (de 1 a 200, por defecto 50) y el <code>
        nextCursor
      </code>
      
       de la página anterior.
    </td>
  </tr>
</tbody>
</table>

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

La respuesta incluye `bookings`, `currency` y `nextCursor: string | null`. Los campos de estado, cliente, coste total y número de reserva de la organización de los elementos de la lista pueden ser `null`.

## `bookings.get()`

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

Usa `idKind: 'org'` cuando `id` es el número de reserva que ve la organización. Si no, omítelo o usa `internal`.

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

La reserva incluye los artículos, los servicios y los pagos. Varios campos de estado, cliente, precio, pasarela y URL de pago pueden ser `null`. `items` lista la cantidad reservada de cada producto, incluso cuando las unidades solo se asignan en la recogida.

`saleItems` lista los productos vendidos con la reserva y `saleId` apunta a su [venta](https://requiply.com/docs/es/developers/typescript-sdk/sales#salesget). Los dos están vacíos o son `null` cuando no se vendió nada o la función de ventas está desactivada. El total de los productos vendidos siempre está incluido en `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` admite hasta 4000 caracteres. La nota es interna: el personal la ve en la reserva y el cliente nunca. La respuesta contiene `noteId`, `reservationId` y `createdAt`, que puede ser nulo. Es una operación de servidor con clave secreta.


## Sitemap

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