# Products

Units that can be booked individually

Endpoint: `https://api.bookingmood.com/v1/products`, supporting `GET`, `PATCH`, `POST`, `DELETE`. Authenticate with the header `Authorization: Bearer YOUR_API_KEY` (see [authentication](https://www.bookingmood.com/it-IT/api-reference/authentication.md)).

## The product object

| Attribute | Type | Description |
| --- | --- | --- |
| `id` | `uuid` | Unique identifier. |
| `ac_id` | `text \| null` | Identifier of the unit on AvailabilityCalendar.com, for imported units. |
| `organization_id` | `uuid` | Identifier of the organization this unit is defined in. References [`organizations.id`](https://www.bookingmood.com/it-IT/api-reference/organizations.md). |
| `created_at` | `timestamp with time zone` | Creation timestamp. Read-only: set by the database. |
| `updated_at` | `timestamp with time zone` | Last modification timestamp. Read-only: set by the database. |
| `deleted_at` | `timestamp with time zone \| null` | Timestamp at which the unit was deleted. |
| `approximate_address` | `text` | Approximate address of the unit. |
| `approximate_coordinates` | `Coordinates \| null` | Coordinates of the approximate address. |
| `cancel_after_initial_payment_error` | `boolean` | Whether to cancel bookings when their initial online payment fails, i.e. expires or is rejected. |
| `confirm_after_initial_payment` | `boolean` | Whether to confirm bookings automatically after the initial payment. Only applies when `interaction` is `request`. |
| `confirmation_message` | `MultiLanguageString` | Localized message shown after submitting a booking. |
| `cooldown_time` | `integer` | Minimum number of days between bookings. |
| `currency` | `Currency` | Currency bookings are made in. |
| `deposit` | `ServiceDeposit \| null` | Deposit charged for rent. |
| `description` | `MultiLanguageString` | Localized description of the unit. |
| `exact_address` | `text` | Exact address of the unit. |
| `exact_coordinates` | `Coordinates \| null` | Coordinates of the exact address. |
| `fee` | `ServiceFee` | Fee charged for rent. |
| `fts` | `tsvector` | Full text search index. Read-only: set by the database. |
| `hide_exact_address` | `boolean` | Whether to hide the exact address from guests until their booking is confirmed. |
| `ical_export_secret` | `text` | Secret used to generate iCal feed URLs. |
| `ical_export_secret_no_details` | `text` | Secret used to generate iCal feed URLs without booking details. |
| `images` | `Array<ProductImage>` | Images of the unit. |
| `interaction` | `BookingMethod` | How guests book the unit: `request` or `book`. |
| `latest_arrival_time` | `time without time zone \| null` | Latest time guests can book for the same day. |
| `name` | `MultiLanguageString` | Localized name of the unit. |
| `payment_expiration_duration` | `integer` | Seconds after which an unpaid online payment expires. |
| `price_breakdown_specificity` | `PriceBreakdownSpecificity` | Whether multiple pricing lines of a single fee are `combined` into one or shown `separate`ly. |
| `rent_period` | `RentPeriod` | Rent period of the unit. |
| `rent_pricing_definition` | `RentPricingDefinition` | How rent is priced: `service` with rent prices, `configurations` with a price calendar, or `pricelabs` with prices from PriceLabs. |
| `request_status` | `CalendarEventStatus` | Status of new booking requests: `TENTATIVE` keeps them pending until they are confirmed, `CONFIRMED` confirms them right away. Doesn't apply to instant bookings. |
| `tentative_interaction` | `BookingMethod` | How guests book tentative dates: `request` or `book`. |
| `timezone` | `text` | Timezone of the unit. |

```json
{
  "id": "00000000-0000-0000-0000-000000000000",
  "ac_id": "",
  "organization_id": "00000000-0000-0000-0000-000000000000",
  "created_at": "2026-10-30T23:18:45.588Z",
  "updated_at": "2026-10-30T23:18:45.588Z",
  "deleted_at": "2026-10-30T23:18:45.588Z",
  "approximate_address": "",
  "approximate_coordinates": {
    "lat": 52.379189,
    "lng": 4.899431
  },
  "cancel_after_initial_payment_error": false,
  "confirm_after_initial_payment": false,
  "confirmation_message": {},
  "cooldown_time": 0,
  "currency": "ARS",
  "deposit": {
    "installments": [
      {
        "id": "00000000-0000-0000-0000-000000000000",
        "moment": {
          "offset": 0,
          "type": "check-in"
        },
        "value": {
          "restrictions": [],
          "type": "absolute",
          "value": [
            {
              "occupancy_group_id": null,
              "period": null,
              "value": 100
            }
          ]
        }
      }
    ],
    "price": {
      "restrictions": [],
      "type": "absolute",
      "value": [
        {
          "occupancy_group_id": null,
          "period": null,
          "value": 200
        }
      ]
    },
    "refund_moment": {
      "offset": 0,
      "type": "checkout"
    },
    "refund_policy": {
      "default": "Full refund",
      "nl-NL": "Volledige terugbetaling"
    },
    "tax_ids": []
  },
  "description": {},
  "exact_address": "",
  "exact_coordinates": {
    "lat": 52.379189,
    "lng": 4.899431
  },
  "fee": {},
  "hide_exact_address": false,
  "ical_export_secret": "md5((random())::text)",
  "ical_export_secret_no_details": "md5((random())::text)",
  "images": [
    {
      "alt": {
        "default": "Image"
      },
      "size": "1x1",
      "src": "https://example.com/image.jpg"
    }
  ],
  "interaction": "request",
  "latest_arrival_time": null,
  "name": {},
  "payment_expiration_duration": 0,
  "price_breakdown_specificity": "combined",
  "rent_period": "nightly",
  "rent_pricing_definition": "configurations",
  "request_status": "TENTATIVE",
  "tentative_interaction": "request",
  "timezone": "UTC"
}
```

### Types

```typescript
type AbsolutePriceExpression = {
  occupancy_group_id: string | null;
  period: PeriodExpression | null;
  value: number;
};

type BookingDateVariable = "arrival_date" | "departure_date";

type BookingMethod = "book" | "request";

type BooleanExpression = 
  | {
      max: number | null;
      min: number | null;
      operator: "duration";
      variable: DurationVariable;
    }
  | {
      operator: "implies";
      then: BooleanExpression;
      when: BooleanExpression;
    }
  | {
      max: number | null;
      min: number | null;
      occupancy_group_id: string;
      operator: "occupancy";
    }
  | {
      operator: "season";
      season_id: string;
      variable: BookingDateVariable;
    }
  | {
      operator: "weekday";
      variable: BookingDateVariable;
      weekdays: Array<Day>;
    };

type CalendarEventStatus = "CONFIRMED" | "TENTATIVE" | "CANCELLED";

type Coordinates = {
  lat: number;
  lng: number;
};

type Currency = 
  | "ARS"
  | "AUD"
  | "BRL"
  | "BYN"
  | "CAD"
  | "CHF"
  | "CLP"
  | "CNY"
  | "COP"
  | "CZK"
  | "DKK"
  | "EGP"
  | "EUR"
  | "GBP"
  | "HKD"
  | "HUF"
  | "ILS"
  | "INR"
  | "JPY"
  | "KRW"
  | "KYD"
  | "MAD"
  | "MXN"
  | "MYR"
  | "NGN"
  | "NOK"
  | "NZD"
  | "PEN"
  | "PHP"
  | "PLN"
  | "RON"
  | "RUB"
  | "SEK"
  | "SGD"
  | "THB"
  | "TRY"
  | "TWD"
  | "UAH"
  | "USD"
  | "XOF"
  | "VND"
  | "ZAR";

type Day = 6 | 0 | 1 | 2 | 3 | 4 | 5;

type DurationVariable = "advance_days" | "booking_days";

type Installment = {
  id: string;
  moment: MomentExpression;
  value: PriceExpression;
};

type Language = 
  | "be-BY"
  | "bg-BG"
  | "cs-CZ"
  | "da-DK"
  | "de-DE"
  | "en-GB"
  | "en-NZ"
  | "en-US"
  | "es-ES"
  | "eu-ES"
  | "fr-FR"
  | "gr-EL"
  | "he-IL"
  | "hr-HR"
  | "hu-HU"
  | "it-IT"
  | "ja-JP"
  | "lt-LT"
  | "lv-LV"
  | "nl-NL"
  | "nn-NO"
  | "pl-PL"
  | "pt-BR"
  | "ro-RO"
  | "ru-RU"
  | "sk-SK"
  | "sl-SI"
  | "sr-Cyrl"
  | "sr-Latn"
  | "sv-SE"
  | "tr-TR"
  | "uk-UA";

type MomentExpression = {
  offset: number;
  type: "check-in" | "checkout" | "confirmation" | "on-site" | "request";
};

type MultiLanguageString = {
  default: string;
} & Partial<Record<Language, string>>;

type PeriodExpression = {
  duration: number;
  start: Array<Day> | null;
};

type PriceBreakdownSpecificity = "combined" | "separate";

type PriceExpression = 
  | {
      restrictions: Array<BooleanExpression>;
      type: "on-request";
    }
  | {
      restrictions: Array<BooleanExpression>;
      type: "absolute";
      value: Array<AbsolutePriceExpression>;
    }
  | {
      restrictions: Array<BooleanExpression>;
      type: "relative";
      value: number;
    }
  | {
      groups: Array<{
        max: number | null;
        min: number | null;
        value: PriceExpression;
      }>;
      occupancy_group_id: string;
      restrictions: Array<BooleanExpression>;
      type: "occupancy-discrimination";
    }
  | {
      groups: Array<{
        season_id: string;
        value: PriceExpression;
      }>;
      restrictions: Array<BooleanExpression>;
      type: "season-discrimination";
      variable: "arrival_date";
    }
  | {
      groups: Array<{
        max: number | null;
        min: number | null;
        value: PriceExpression;
      }>;
      restrictions: Array<BooleanExpression>;
      type: "duration-discrimination";
      variable: DurationVariable;
    };

type ProductImage = {
  alt: MultiLanguageString;
  size?: "1x1" | "1x2" | "2x2";
  src: string;
};

type RentPeriod = "nightly" | "daily";

type RentPricingDefinition = "configurations" | "pricelabs" | "service";

type ServiceDeposit = {
  installments: Array<Installment>;
  name?: MultiLanguageString;
  price: PriceExpression;
  refund_moment: MomentExpression | null;
  refund_policy: MultiLanguageString;
  tax_ids: Array<string>;
};

type ServiceDiscount = {
  definition: PriceExpression;
  id: string;
  name: MultiLanguageString;
};

type ServiceFee = {
  discounts: Array<ServiceDiscount>;
  installments: Array<Installment>;
  name?: MultiLanguageString;
  price: PriceExpression;
  tax_ids: Array<string>;
};
```

### Related resources

Embed related resources with the [`select` parameter](https://www.bookingmood.com/it-IT/api-reference/selecting.md), e.g. `select=*,organizations!organization_id(*)`. Resources related through more than one column need the column as a hint, like `table!column(*)`.

| Embed | Type | Description |
| --- | --- | --- |
| `organizations!organization_id(*)` | `object \| null` | The [organization](https://www.bookingmood.com/it-IT/api-reference/organizations.md) referenced by `organization_id`. |
| `calendar_event_updates!next_product_id(*)` | `array` | The [calendar event updates](https://www.bookingmood.com/it-IT/api-reference/calendar_event_updates.md) referencing this product through `calendar_event_updates.next_product_id`. |
| `calendar_event_updates!prev_product_id(*)` | `array` | The [calendar event updates](https://www.bookingmood.com/it-IT/api-reference/calendar_event_updates.md) referencing this product through `calendar_event_updates.prev_product_id`. |
| `calendar_events!product_id(*)` | `array` | The [calendar events](https://www.bookingmood.com/it-IT/api-reference/calendar_events.md) referencing this product through `calendar_events.product_id`. |
| `capacities(*)` | `array` | The [capacities](https://www.bookingmood.com/it-IT/api-reference/capacities.md) referencing this product through `capacities.product_id`. |
| `coupon_products(*)` | `array` | The [coupon products](https://www.bookingmood.com/it-IT/api-reference/coupon_products.md) referencing this product through `coupon_products.product_id`. |
| `external_calendars!product_id(*)` | `array` | The [external calendars](https://www.bookingmood.com/it-IT/api-reference/external_calendars.md) referencing this product through `external_calendars.product_id`. |
| `permissions(*)` | `array` | The [permissions](https://www.bookingmood.com/it-IT/api-reference/permissions.md) referencing this product through `permissions.product_id`. |
| `price_calendar_entries(*)` | `array` | The [price calendar entries](https://www.bookingmood.com/it-IT/api-reference/price_calendar_entries.md) referencing this product through `price_calendar_entries.product_id`. |
| `pricelabs_pricing(*)` | `array` | The [pricelabs pricing](https://www.bookingmood.com/it-IT/api-reference/pricelabs_pricing.md) referencing this product through `pricelabs_pricing.product_id`. |
| `pricing_widgets(*)` | `array` | The [pricing widgets](https://www.bookingmood.com/it-IT/api-reference/pricing_widgets.md) referencing this product through `pricing_widgets.product_id`. |
| `product_attribute_options(*)` | `array` | The [product attribute options](https://www.bookingmood.com/it-IT/api-reference/product_attribute_options.md) referencing this product through `product_attribute_options.product_id`. |
| `product_calendar_logs(*)` | `array` | The [product calendar logs](https://www.bookingmood.com/it-IT/api-reference/product_calendar_logs.md) referencing this product through `product_calendar_logs.product_id`. |
| `product_message_templates(*)` | `array` | The [product message templates](https://www.bookingmood.com/it-IT/api-reference/product_message_templates.md) referencing this product through `product_message_templates.product_id`. |
| `product_reply_to_addresses(*)` | `array` | The [product reply to addresses](https://www.bookingmood.com/it-IT/api-reference/product_reply_to_addresses.md) referencing this product through `product_reply_to_addresses.product_id`. |
| `product_services(*)` | `array` | The [product services](https://www.bookingmood.com/it-IT/api-reference/product_services.md) referencing this product through `product_services.product_id`. |
| `review_products(*)` | `array` | The [review products](https://www.bookingmood.com/it-IT/api-reference/review_products.md) referencing this product through `review_products.product_id`. |
| `review_widget_listings(*)` | `array` | The [review widget listings](https://www.bookingmood.com/it-IT/api-reference/review_widget_listings.md) referencing this product through `review_widget_listings.product_id`. |
| `site_listings(*)` | `array` | The [site listings](https://www.bookingmood.com/it-IT/api-reference/site_listings.md) referencing this product through `site_listings.product_id`. |
| `task_templates!product_id(*)` | `array` | The [task templates](https://www.bookingmood.com/it-IT/api-reference/task_templates.md) referencing this product through `task_templates.product_id`. |
| `widget_listings(*)` | `array` | The [widget listings](https://www.bookingmood.com/it-IT/api-reference/widget_listings.md) referencing this product through `widget_listings.product_id`. |

## List products

`GET /v1/products`

Filter on any attribute with `column=operator.value` (see [filtering](https://www.bookingmood.com/it-IT/api-reference/filtering.md)). Choose columns and embed related resources with `select` (see [selecting](https://www.bookingmood.com/it-IT/api-reference/selecting.md)), and sort and paginate with `order`, `limit` and `offset` (see [sorting and pagination](https://www.bookingmood.com/it-IT/api-reference/sorting-pagination.md)).

```bash
curl -X GET "https://api.bookingmood.com/v1/products?select=*&limit=10" \
  -H "Authorization: Bearer YOUR_API_KEY"
```

Responses:

- `200`: A list of products with the selected columns. `206` when the list is a partial range.
- `400`: The request is malformed, for example an unknown column or an invalid filter.
- `401`: The API key is missing or invalid.

## Update products

`PATCH /v1/products`

Filter on any attribute with `column=operator.value` (see [filtering](https://www.bookingmood.com/it-IT/api-reference/filtering.md)). Without filters, this request updates all products you have access to. Always filter on at least one column, e.g. `id=eq.{id}`. The body holds the attributes to change.

```bash
curl -X PATCH "https://api.bookingmood.com/v1/products?id=eq.00000000-0000-0000-0000-000000000000" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "approximate_address": ""
  }'
```

Responses:

- `200`: The updated products with the selected columns. `204` without a body when sending the header `Prefer: return=minimal`.
- `400`: The request is malformed, for example an unknown column, an invalid filter or an invalid body.
- `401`: The API key is missing or invalid.
- `403`: The API key has no permission for this change, for example when writing rows of another organization.
- `409`: The change conflicts with existing data, for example a duplicate key or a reference to a row that does not exist.

## Create product

`POST /v1/products`

Register a new product in your organization with sensible defaults, which can be changed by updating the product. This action automatically updates your subscription. Responds with `201` and the newly created product.

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `name` | `MultiLanguageString` | Yes | The product name. Keys are language codes, e.g. `{"default": "Cabin", "nl-NL": "Hut"}`. |
| `rent_period` | `"nightly" \| "daily"` | Yes | The period by which the product is rented out. |
| `timezone` | `string` | Yes | The IANA timezone of the product, e.g. `Europe/Amsterdam`. |

```bash
curl -X POST "https://api.bookingmood.com/v1/products" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": {
      "default": "Summer cottage"
    },
    "rent_period": "nightly",
    "timezone": "Europe/Amsterdam"
  }'
```

## Delete product

`DELETE /v1/products?id={id}`

Delete a product and all of its related data. Responds with the deleted product.

```bash
curl -X DELETE "https://api.bookingmood.com/v1/products?id=00000000-0000-0000-0000-000000000000" \
  -H "Authorization: Bearer YOUR_API_KEY"
```

Errors have a JSON body with a `code`, `message`, `details` and `hint`. See the [introduction](https://www.bookingmood.com/it-IT/api-reference.md) for more on errors. The full API is described by the OpenAPI spec at https://api.bookingmood.com/v1/openapi.json.
