# Widgets

Embeddable widgets

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

## The widget object

| Attribute | Type | Description |
| --- | --- | --- |
| `id` | `uuid` | Unique identifier. |
| `organization_id` | `uuid` | Identifier of the organization this widget is defined in. References [`organizations.id`](https://www.bookingmood.com/sv-SE/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. |
| `autocomplete_date_selection` | `boolean` | Whether to autocomplete the date selection when only one date is available. |
| `currency` | `Currency \| null` | Currency of the widget. |
| `date_visibility` | `WidgetDateVisibility` | Which dates the calendar shows: `all`, `hide-closed` to hide closed dates, `hide-unavailable` to hide unavailable dates, or the deprecated `custom` to show selected months. |
| `enable_shopping_cart` | `boolean` | Whether guests can book multiple units at once. |
| `failure_url` | `text` | URL to redirect to when a payment fails. `__BOOKING_PAGE__` shows the guest page. |
| `font` | `text \| null` | Font of the widget. |
| `fts` | `tsvector \| null` | Full text search index. Read-only: set by the database. |
| `initial_date` | `WidgetInitialDate` | Initial date shown in the calendar. |
| `interaction` | `ChannelInteraction \| null` | What happens when a guest picks dates: `book` to book in the widget, or `forward` and `forward-same-tab` to open the listing URL in a new or the same tab. |
| `language` | `Language` | Language of the widget. |
| `redirect_url` | `text \| null` | URL to redirect to after submitting a booking. |
| `settings` | `CalendarWidgetSettings \| TimelineWidgetSettings \| InventoryWidgetSettings \| SearchWidgetSettings` | Settings of the widget. |
| `show_booked_as` | `ShowBookedAs` | How to show booked dates. |
| `show_bookingmood_branding` | `boolean` | Whether to show the Bookingmood branding. |
| `show_closed_as` | `ShowClosedAs` | How to show closed dates. |
| `show_complex_prices` | `boolean` | Whether guests can inspect seasonal prices in the calendar. |
| `show_past` | `boolean` | Whether to show past dates in the calendar. |
| `show_pending_as` | `CalendarEventStatus` | How to interpret pending events. |
| `show_reviews` | `boolean` | Whether to show reviews. |
| `stay_expanded` | `boolean` | Whether the date-selection section should stay expanded after selecting dates. |
| `title` | `text` | Title of the widget. |
| `type` | `"calendar" \| "timeline" \| "inventory" \| "search"` | Type of the widget: `calendar`, `timeline`, `inventory` or `search`. |
| `visible_months` | `[boolean, boolean, boolean, boolean, boolean, boolean, boolean, boolean, boolean, boolean, boolean, boolean] \| null` | Months that are visible in the calendar. Deprecated. |

```json
{
  "id": "00000000-0000-0000-0000-000000000000",
  "organization_id": "00000000-0000-0000-0000-000000000000",
  "created_at": "2026-10-30T23:37:06.265Z",
  "updated_at": "2026-10-30T23:37:06.265Z",
  "autocomplete_date_selection": false,
  "currency": "USD",
  "date_visibility": "all",
  "enable_shopping_cart": false,
  "failure_url": "__BOOKING_PAGE__",
  "font": "",
  "initial_date": "today",
  "interaction": "book",
  "language": "en-US",
  "redirect_url": "https://example.com",
  "settings": {},
  "show_booked_as": "unavailable",
  "show_bookingmood_branding": true,
  "show_closed_as": "unavailable",
  "show_complex_prices": true,
  "show_past": false,
  "show_pending_as": "TENTATIVE",
  "show_reviews": false,
  "stay_expanded": false,
  "title": "Summer cottage",
  "type": "calendar",
  "visible_months": [
    false
  ]
}
```

### Types

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

type CalendarWidgetSettings = {
  color_available: string;
  color_booked: string;
  color_closed: string;
  color_error: string;
  color_info: string;
  color_primary: string;
  color_surface: string;
  color_tentative: string;
  color_text: string;
  color_unavailable: string;
  color_warning: string;
  first_week_contains_date: 1 | 4 | null;
  week_starts_on: Day | null;
  display_legend: boolean;
  display_product_images: boolean;
  display_product_name: boolean;
  display_week_numbers: boolean;
  interval_wrap_style: ChannelIntervalWrapStyle;
  number_of_months: integer | null;
  rate_location: ChannelRateLocation;
  rate_visibility: ChannelRateVisibility;
  show_totals: boolean;
  show_yearly: boolean;
  size: "compact" | "regular";
  theme: "modern" | "classic";
};

type ChannelInteraction = "forward" | "forward-same-tab" | "book";

type ChannelIntervalWrapStyle = "square" | "jagged";

type ChannelRateLocation = "inline" | "tooltip";

type ChannelRateVisibility = "all" | "available" | "none";

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 InventoryWidgetSettings = {
  color_available: string;
  color_booked: string;
  color_closed: string;
  color_error: string;
  color_info: string;
  color_primary: string;
  color_surface: string;
  color_tentative: string;
  color_text: string;
  color_unavailable: string;
  color_warning: string;
  first_week_contains_date: 1 | 4 | null;
  week_starts_on: Day | null;
  autoselect_random_unit: boolean;
  display_legend: boolean;
  display_product_details: boolean;
  display_week_numbers: boolean;
  grouping_attribute_id: uuid | null;
  interval_wrap_style: ChannelIntervalWrapStyle;
  number_of_months: integer | null;
  rate_location: ChannelRateLocation;
  rate_visibility: ChannelRateVisibility;
  round_search_result_prices: boolean;
  search_result_layout: "list" | "grid" | "map";
  show_rates: boolean;
  show_unavailable_products: boolean;
  show_yearly: boolean;
  size: "compact" | "regular";
  theme: "modern" | "classic";
};

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 SearchWidgetSettings = {
  color_available: string;
  color_booked: string;
  color_closed: string;
  color_error: string;
  color_info: string;
  color_primary: string;
  color_surface: string;
  color_tentative: string;
  color_text: string;
  color_unavailable: string;
  color_warning: string;
  first_week_contains_date: 1 | 4 | null;
  week_starts_on: Day | null;
  autoselect_random_unit: boolean;
  display_product_details: boolean;
  filters: Array<string>;
  grouping_attribute_id: uuid | null;
  rate_location: ChannelRateLocation;
  rate_visibility: ChannelRateVisibility;
  round_search_result_prices: boolean;
  search_result_layouts: Array<"list" | "grid" | "map">;
  show_unavailable_products: boolean;
  size: "small" | "regular";
};

type ShowBookedAs = "booked" | "unavailable";

type ShowClosedAs = "closed" | "unavailable";

type TimelineWidgetSettings = {
  color_available: string;
  color_booked: string;
  color_closed: string;
  color_error: string;
  color_info: string;
  color_primary: string;
  color_surface: string;
  color_tentative: string;
  color_text: string;
  color_unavailable: string;
  color_warning: string;
  first_week_contains_date: 1 | 4 | null;
  week_starts_on: Day | null;
  display_legend: boolean;
  display_product_covers: boolean;
  display_product_details: boolean;
  display_weekdays: boolean;
  max_height: integer | null;
  rate_location: ChannelRateLocation;
  rate_visibility: ChannelRateVisibility;
  show_totals: boolean;
  size: "responsive" | "compact" | "wide";
  theme: "modern" | "classic";
};

type WidgetDateVisibility = "all" | "hide-unavailable" | "hide-closed" | "custom";

type WidgetInitialDate = "today" | "next-available";
```

### Related resources

Embed related resources with the [`select` parameter](https://www.bookingmood.com/sv-SE/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/sv-SE/api-reference/organizations.md) referenced by `organization_id`. |
| `bookings(*)` | `array` | The [bookings](https://www.bookingmood.com/sv-SE/api-reference/bookings.md) referencing this widget through `bookings.widget_id`. |
| `widget_listings(*)` | `array` | The [widget listings](https://www.bookingmood.com/sv-SE/api-reference/widget_listings.md) referencing this widget through `widget_listings.widget_id`. |

## List widgets

`GET /v1/widgets`

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

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

Responses:

- `200`: A list of widgets 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.

## Create widgets

`POST /v1/widgets`

Insert one widget or an array of widgets. The body is a `Partial` widget object, so columns with a default can be left out. Read-only attributes can't be written.

```bash
curl -X POST "https://api.bookingmood.com/v1/widgets" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "organization_id": "00000000-0000-0000-0000-000000000000",
    "autocomplete_date_selection": false,
    "date_visibility": "all",
    "enable_shopping_cart": false,
    "failure_url": "__BOOKING_PAGE__",
    "initial_date": "today",
    "language": "en-US",
    "settings": {},
    "show_booked_as": "unavailable",
    "show_bookingmood_branding": true,
    "show_closed_as": "unavailable",
    "show_complex_prices": true,
    "show_past": false,
    "show_pending_as": "TENTATIVE",
    "show_reviews": false,
    "stay_expanded": false,
    "title": "Summer cottage",
    "type": "calendar"
  }'
```

Responses:

- `201`: The newly created widgets with the selected columns.
- `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.

## Delete widgets

`DELETE /v1/widgets`

Filter on any attribute with `column=operator.value` (see [filtering](https://www.bookingmood.com/sv-SE/api-reference/filtering.md)). Without filters, this request deletes all widgets you have access to. Always filter on at least one column, e.g. `id=eq.{id}`.

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

Responses:

- `200`: The deleted widgets 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 or an invalid filter.
- `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.

## Update widgets

`PATCH /v1/widgets`

Filter on any attribute with `column=operator.value` (see [filtering](https://www.bookingmood.com/sv-SE/api-reference/filtering.md)). Without filters, this request updates all widgets 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/widgets?id=eq.00000000-0000-0000-0000-000000000000" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "autocomplete_date_selection": false
  }'
```

Responses:

- `200`: The updated widgets 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.

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