# Dates (`date_range`)

A calendar for choosing one date or a date range (stays, appointments, deadlines).

**When to use it:** The person picks a date, or a check-in and check-out range, on a calendar with limits and unavailable days.

**When not to use it:** Do not use it when you already have specific times or a few fixed options to offer (use `time_slots`), or for a single date the person can just type in chat.

**Gather first:**

- Whether you need one date (`mode: "single"`) or a stay from start to end (`"range"`).
- The earliest and latest dates you can work with, and any dates that are not possible.

**Writing good content:**

- Set `minDate` and `maxDate` to the real window and list impossible days in `disabledDates`, so the person cannot pick a date you cannot use.
- For stays, set `minNights` and `maxNights` when the place requires them. For any other period (a trip, time off, a deadline window) set `stay: false`, so the page does not count nights.
- Use `dayNotes` for a very short note under a day, such as a nightly price, only when you have it for those days.

## Fields

Every page also has `version` (always `1`), `title`, and optionally `description`, `branding`, `cover` (a header photo) and `expiresAt`; see [Page fields](/docs/api#page-fields). These are the fields of `page` (the page type and its data):

| Field | Description |
| --- | --- |
| `mode` | *"single" \| "range", required.* "single" picks one date; "range" picks a start and an end date. |
| `minDate` | *string.* Earliest selectable date. Days before it are disabled. Defaults to no limit. |
| `maxDate` | *string.* Latest selectable date. Days after it are disabled. Defaults to no limit. |
| `disabledDates` | *string[].* Dates that cannot be chosen, e.g. days with no availability. A range cannot span a disabled date. Limits: at most 400 items. |
| `minNights` | *integer.* Range mode only: fewest nights (endDate minus startDate). Defaults to 1. Limits: 1-365. |
| `maxNights` | *integer.* Range mode only: most nights. Defaults to no limit. Limits: 1-365. |
| `stay` | *boolean.* Range mode only. Omit or true for a stay (check-in to check-out): the page counts and shows nights. false for any other period (a trip, time off, a deadline window): no night count, and minNights/maxNights read as days between the dates. |
| `dayNotes` | *object[].* Optional small note per date, usually a price, e.g. { "date": "2026-11-03", "note": "$129" }. Limits: at most 400 items. |
| `dayNotes[].date` | *string, required.* Calendar date as "YYYY-MM-DD", e.g. "2026-11-03". No time or time zone. |
| `dayNotes[].note` | *string, required.* Very short text under the day number, e.g. "$129". Max 8 characters. Limits: 1-8 characters. |
| `defaultValue` | *object.* Optional preselected answer. Use { date } in single mode or { startDate, endDate } in range mode. |
| `defaultValue (variant 1).date` | *string, required.* The chosen date, "YYYY-MM-DD". |
| `defaultValue (variant 2).startDate` | *string, required.* First day of the range (check-in), "YYYY-MM-DD". |
| `defaultValue (variant 2).endDate` | *string, required.* Last day of the range (check-out), "YYYY-MM-DD". Nights = endDate minus startDate. |

**Why these are required:** It decides the result shape (`{ date }` or `{ startDate, endDate }`).

**When optional fields are left out:** No limits; at least 1 night in range mode; a range is a stay and the page counts nights (`stay: false` drops the night count for other periods); nothing preselected.

## Minimal example

Only the required fields:

```json
{
  "version": 1,
  "title": "When should the piano tuner come?",
  "page": {
    "type": "date_range",
    "mode": "single"
  }
}
```

## Full example

```json
{
  "version": 1,
  "title": "Pick your dates at Harbor Light Inn",
  "description": "Nightly rates are shown under each day. Stays are 2 to 7 nights.",
  "branding": {
    "name": "Juniper Concierge",
    "accentColor": "#0050d8"
  },
  "page": {
    "type": "date_range",
    "mode": "range",
    "minDate": "2026-11-02",
    "maxDate": "2027-01-31",
    "minNights": 2,
    "maxNights": 7,
    "disabledDates": [
      "2026-11-12",
      "2026-11-13",
      "2026-11-27"
    ],
    "dayNotes": [
      {
        "date": "2026-11-03",
        "note": "$129"
      },
      {
        "date": "2026-11-04",
        "note": "$129"
      },
      {
        "date": "2026-11-05",
        "note": "$139"
      },
      {
        "date": "2026-11-06",
        "note": "$159"
      },
      {
        "date": "2026-11-07",
        "note": "$189"
      },
      {
        "date": "2026-11-08",
        "note": "$189"
      },
      {
        "date": "2026-11-09",
        "note": "$149"
      },
      {
        "date": "2026-11-10",
        "note": "$129"
      },
      {
        "date": "2026-11-11",
        "note": "$129"
      },
      {
        "date": "2026-11-12",
        "note": "$135"
      },
      {
        "date": "2026-11-13",
        "note": "$155"
      },
      {
        "date": "2026-11-14",
        "note": "$185"
      },
      {
        "date": "2026-11-15",
        "note": "$189"
      },
      {
        "date": "2026-11-16",
        "note": "$149"
      },
      {
        "date": "2026-11-17",
        "note": "$129"
      },
      {
        "date": "2026-11-18",
        "note": "$129"
      },
      {
        "date": "2026-11-19",
        "note": "$129"
      },
      {
        "date": "2026-11-20",
        "note": "$139"
      }
    ]
  }
}
```

## Result

When the person submits, the result has these fields:

| Field | Description |
| --- | --- |
| ` (variant 1).date` | *string, required.* The chosen date, "YYYY-MM-DD". |
| ` (variant 2).startDate` | *string, required.* First day of the range (check-in), "YYYY-MM-DD". |
| ` (variant 2).endDate` | *string, required.* Last day of the range (check-out), "YYYY-MM-DD". Nights = endDate minus startDate. |

For the full example above, a result could be:

```json
{
  "startDate": "2026-11-03",
  "endDate": "2026-11-06"
}
```

## Send it

### eve, AI SDK and MCP

With [`@pagesystems/eve`](/docs#quickstart-eve), [`@pagesystems/ai-sdk`](/docs#quickstart-ai-sdk) or the [MCP server](/docs#quickstart-mcp), the model calls `get_page_type_schema` with `"date_range"` (the same JSON as [`GET /v1/page-types/date_range`](/docs/api#endpoints)), then `create_page` with this input (no `version` or `branding`; branding comes from your dashboard):

```json
{
  "title": "Pick your dates at Harbor Light Inn",
  "description": "Nightly rates are shown under each day. Stays are 2 to 7 nights.",
  "page": {
    "type": "date_range",
    "mode": "range",
    "minDate": "2026-11-02",
    "maxDate": "2027-01-31",
    "minNights": 2,
    "maxNights": 7,
    "disabledDates": [
      "2026-11-12",
      "2026-11-13",
      "2026-11-27"
    ],
    "dayNotes": [
      {
        "date": "2026-11-03",
        "note": "$129"
      },
      {
        "date": "2026-11-04",
        "note": "$129"
      },
      {
        "date": "2026-11-05",
        "note": "$139"
      },
      {
        "date": "2026-11-06",
        "note": "$159"
      },
      {
        "date": "2026-11-07",
        "note": "$189"
      },
      {
        "date": "2026-11-08",
        "note": "$189"
      },
      {
        "date": "2026-11-09",
        "note": "$149"
      },
      {
        "date": "2026-11-10",
        "note": "$129"
      },
      {
        "date": "2026-11-11",
        "note": "$129"
      },
      {
        "date": "2026-11-12",
        "note": "$135"
      },
      {
        "date": "2026-11-13",
        "note": "$155"
      },
      {
        "date": "2026-11-14",
        "note": "$185"
      },
      {
        "date": "2026-11-15",
        "note": "$189"
      },
      {
        "date": "2026-11-16",
        "note": "$149"
      },
      {
        "date": "2026-11-17",
        "note": "$129"
      },
      {
        "date": "2026-11-18",
        "note": "$129"
      },
      {
        "date": "2026-11-19",
        "note": "$129"
      },
      {
        "date": "2026-11-20",
        "note": "$139"
      }
    ]
  }
}
```

### REST

```bash
curl https://pagesystems.ai/v1/pages \
  -H "Authorization: Bearer $PAGE_SYSTEMS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"spec":{"version":1,"title":"Pick your dates at Harbor Light Inn","description":"Nightly rates are shown under each day. Stays are 2 to 7 nights.","branding":{"name":"Juniper Concierge","accentColor":"#0050d8"},"page":{"type":"date_range","mode":"range","minDate":"2026-11-02","maxDate":"2027-01-31","minNights":2,"maxNights":7,"disabledDates":["2026-11-12","2026-11-13","2026-11-27"],"dayNotes":[{"date":"2026-11-03","note":"$129"},{"date":"2026-11-04","note":"$129"},{"date":"2026-11-05","note":"$139"},{"date":"2026-11-06","note":"$159"},{"date":"2026-11-07","note":"$189"},{"date":"2026-11-08","note":"$189"},{"date":"2026-11-09","note":"$149"},{"date":"2026-11-10","note":"$129"},{"date":"2026-11-11","note":"$129"},{"date":"2026-11-12","note":"$135"},{"date":"2026-11-13","note":"$155"},{"date":"2026-11-14","note":"$185"},{"date":"2026-11-15","note":"$189"},{"date":"2026-11-16","note":"$149"},{"date":"2026-11-17","note":"$129"},{"date":"2026-11-18","note":"$129"},{"date":"2026-11-19","note":"$129"},{"date":"2026-11-20","note":"$139"}]}}}'
```

## Validation errors

A spec that does not validate is refused with `400 invalid_spec`. The message names each problem and its path, so a model can fix the spec and try again. For example:

**Leaving out the required field `mode`:**

```text
✖ Invalid option: expected one of "single"|"range"
  → at page.mode
```

**Misspelling a field name (unknown keys are rejected):**

```text
✖ Unrecognized key: "colour"
  → at page
```
