# Create booking link

Create a personal booking page where invitees can choose a free slot on your calendar. Requires a connected calendar with permission to create events.

POST https://api.brigg.app/v1/operations/meetings.CreateBookingLink

## Access & visibility

Allow `meetings.CreateBookingLink` on your API key.

**Acting member required.** Send `Brigg-Act-As` with an [approved member account ID](https://developer.brigg.app/authentication#member-delegation). The key supplies permissions; the member selects whose data is used.

The selected member must be active and approved by the credential. This selects whose private data is used; the credential supplies permissions. Private ownership and project boundaries still apply.

[Set up authentication](https://developer.brigg.app/authentication) · [Choose permissions](https://developer.brigg.app/permissions)

## Request fields

Send a JSON object with the following fields. Required fields must be included; omit optional fields you do not need.
| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `name` | string | Yes | Meeting name displayed on the public booking page. At least 1 character. At most 120 characters. |
| `description` | string | No | Introduction displayed to invitees. Send an empty string to clear it. At most 2000 characters. |
| `duration_minutes` | integer | No | Fixed meeting length in minutes. Supplying this alone clears any previous choice of lengths. duration_options takes precedence when both are supplied. Default: `30`. Range: 10 to 480. |
| `duration_options` | array of integer | No | Offered lengths in minutes. One length makes a fixed-duration link; several let the invitee choose. The stored duration_minutes is the shortest offered length. At least 1 item. At most 4 items. |
| `location_kind` | string | No | video creates a conference link; phone, in_person and custom use location_detail for instructions or an address. Allowed: `"video"`, `"phone"`, `"in_person"`, `"custom"`. Default: `"video"`. |
| `location_detail` | string | No | Instructions for the chosen location, such as a phone number or street address. Send an empty string to clear them. At most 500 characters. |
| `timezone` | string | No | An IANA time zone; refused invalid_timezone otherwise. Default: `"Europe/Oslo"`. |
| `availability` | [object](#request-availability) | No | Complete weekly availability in the link's IANA timezone. Omitted or empty weekdays within this object are unavailable. This replaces the whole schedule, not only the listed days. |
| `window_days` | integer | No | Number of days into the future an invitee can book. Default: `30`. Range: 1 to 90. |
| `min_notice_minutes` | integer | No | Minimum notice required before a meeting starts, in minutes. Default: `240`. Range: 0 to 10080. |
| `buffer_before_minutes` | integer | No | Minutes reserved before each meeting. Default: `0`. Range: 0 to 240. |
| `buffer_after_minutes` | integer | No | Minutes reserved after each meeting. Default: `0`. Range: 0 to 240. |
| `max_per_day` | integer | No | Maximum bookings in a day. Zero means no daily limit. Default: `0`. Range: 0 to 50. |
| `questions` | array of [object](#request-questions) | No | Complete replacement question list. Send [] to remove all questions; omit the field on an update to preserve them. At most 5 items. |
| `reminder_hours` | integer | No | Hours before a meeting to send a reminder. Zero disables reminders. Default: `24`. Range: 0 to 336. |
| `collect_phone` | boolean | No | Whether the booking form asks invitees for a phone number. Default: `false`. |
| `active` | boolean | No | Whether new bookings are accepted. False does not cancel confirmed appointments. Setting true requires the acting member's calendar write permission. Default: `true`. |

### request.availability

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `mon` | array of [object](#request-availability-mon) | No | Monday availability intervals in the booking link's timezone. At most 6 items. |
| `tue` | array of [object](#request-availability-tue) | No | Tuesday availability intervals in the booking link's timezone. At most 6 items. |
| `wed` | array of [object](#request-availability-wed) | No | Wednesday availability intervals in the booking link's timezone. At most 6 items. |
| `thu` | array of [object](#request-availability-thu) | No | Thursday availability intervals in the booking link's timezone. At most 6 items. |
| `fri` | array of [object](#request-availability-fri) | No | Friday availability intervals in the booking link's timezone. At most 6 items. |
| `sat` | array of [object](#request-availability-sat) | No | Saturday availability intervals in the booking link's timezone. At most 6 items. |
| `sun` | array of [object](#request-availability-sun) | No | Sunday availability intervals in the booking link's timezone. At most 6 items. |

### request.availability.mon[]

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `start` | string | Yes | Start of the interval. Pattern: `^([01]\d\|2[0-3]):[0-5]\d$`. |
| `end` | string | Yes | End of the interval; must be later than its start. Pattern: `^([01]\d\|2[0-3]):[0-5]\d$`. |

### request.availability.tue[]

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `start` | string | Yes | Start of the interval. Pattern: `^([01]\d\|2[0-3]):[0-5]\d$`. |
| `end` | string | Yes | End of the interval; must be later than its start. Pattern: `^([01]\d\|2[0-3]):[0-5]\d$`. |

### request.availability.wed[]

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `start` | string | Yes | Start of the interval. Pattern: `^([01]\d\|2[0-3]):[0-5]\d$`. |
| `end` | string | Yes | End of the interval; must be later than its start. Pattern: `^([01]\d\|2[0-3]):[0-5]\d$`. |

### request.availability.thu[]

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `start` | string | Yes | Start of the interval. Pattern: `^([01]\d\|2[0-3]):[0-5]\d$`. |
| `end` | string | Yes | End of the interval; must be later than its start. Pattern: `^([01]\d\|2[0-3]):[0-5]\d$`. |

### request.availability.fri[]

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `start` | string | Yes | Start of the interval. Pattern: `^([01]\d\|2[0-3]):[0-5]\d$`. |
| `end` | string | Yes | End of the interval; must be later than its start. Pattern: `^([01]\d\|2[0-3]):[0-5]\d$`. |

### request.availability.sat[]

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `start` | string | Yes | Start of the interval. Pattern: `^([01]\d\|2[0-3]):[0-5]\d$`. |
| `end` | string | Yes | End of the interval; must be later than its start. Pattern: `^([01]\d\|2[0-3]):[0-5]\d$`. |

### request.availability.sun[]

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `start` | string | Yes | Start of the interval. Pattern: `^([01]\d\|2[0-3]):[0-5]\d$`. |
| `end` | string | Yes | End of the interval; must be later than its start. Pattern: `^([01]\d\|2[0-3]):[0-5]\d$`. |

### request.questions[]

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | string | Yes | Stable question key you choose, unique within this link, such as project_goal. Reuse it when editing that question; no create-record call is needed. At least 1 character. At most 40 characters. |
| `label` | string | Yes | Text shown for this booking question or choice. At least 1 character. At most 200 characters. |
| `kind` | string | Yes | Question control: text is a short answer, textarea a longer answer, select a choice from options, and checkbox a yes/no response. Allowed: `"text"`, `"textarea"`, `"select"`, `"checkbox"`. |
| `required` | boolean | No | Whether the invitee must answer this booking question. |
| `options` | array of string | No | Choices for a select question; at least one is required when kind=select. Omit for other controls. At least 1 item. At most 12 items. |

## Prepare the host's calendar first

The approved acting member must connect a calendar with write access before creating a link, even when active=false. Creation defaults apply only here; later updates preserve omitted fields. The response's url is the public page to share with invitees. The URL alone does not guarantee there is an available slot: local availability, calendar conflicts, notice, buffers and booking limits all affect the choices shown.

## Booking links and individual bookings

A booking link belongs to the acting member and advertises availability in its IANA `timezone`. Availability times are local wall-clock times in that zone; confirmed bookings use numeric timestamps. Use [List booking links](https://developer.brigg.app/reference/meetings/ListBookingLinks) to read both links (`items`) and their confirmed bookings starting in the next 366 days (`upcoming_bookings`).

The link ID changes or deactivates the booking page. An individual booking ID cancels one confirmed appointment. Deactivating a link stops new bookings without cancelling appointments already made. A link with future bookings cannot be deleted; deactivate it or cancel those bookings first. Active links require a connected writable calendar.

## Response

Returns **200 OK** with a JSON body.
| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `link` | [BookingLink](#bookinglink-response-link) | Yes | The link row plus its public url. |

### BookingLink · response.link

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | string | Yes | Booking link ID. Use it to update or delete this link. |
| `org_id` | string | Yes | Workspace that owns the link. |
| `owner_account_id` | string | Yes | Member whose calendar supplies the available time slots. |
| `slug` | string | Yes | Unique URL segment for this link within the workspace. |
| `name` | string | Yes | Meeting name displayed to invitees. |
| `description` | string or null | Yes | Introduction displayed on the booking page. |
| `duration_minutes` | integer | Yes | Default meeting length in minutes; the shortest length when several are offered. |
| `duration_options` | array of integer or null | Yes | Offered meeting lengths in minutes. Null means a fixed duration_minutes. |
| `location_kind` | string | Yes | video creates a conference link; phone, in_person and custom use location_detail for instructions or an address. Allowed: `"video"`, `"phone"`, `"in_person"`, `"custom"`. |
| `location_detail` | string or null | Yes | Address, phone instructions or other location details. |
| `timezone` | string | Yes | IANA time zone used to interpret the weekly availability, for example Europe/Oslo. |
| `availability` | [WeeklyAvailability](#weeklyavailability-response-link-availability) | Yes | Bookable local-time intervals by weekday. Missing or empty days are unavailable. |
| `window_days` | integer | Yes | How many days into the future invitees can book. |
| `min_notice_minutes` | integer | Yes | Minimum time before a meeting can start, in minutes. |
| `buffer_before_minutes` | integer | Yes | Minutes reserved before each meeting. |
| `buffer_after_minutes` | integer | Yes | Minutes reserved after each meeting. |
| `max_per_day` | integer | Yes | Maximum bookings per day. Zero means no daily limit. |
| `questions` | array of [object](#response-link-questions) or null | Yes | Additional questions shown to invitees. Null means no questions. |
| `reminder_hours` | integer | Yes | Hours before the meeting to send a reminder. Zero disables reminders. |
| `collect_phone` | boolean | Yes | Whether invitees are asked for a phone number. |
| `active` | boolean | Yes | Whether invitees can make new bookings through this link. |
| `created_at` | integer | Yes | Creation time in Unix milliseconds. |
| `updated_at` | integer | Yes | Last change in Unix milliseconds. |
| `url` | string | Yes | Public booking URL to share with invitees. Format: uri. |

### WeeklyAvailability · response.link.availability

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `mon` | array of [object](#response-link-availability-mon) | No | Monday availability intervals in the booking link's timezone. At most 6 items. |
| `tue` | array of [object](#response-link-availability-tue) | No | Tuesday availability intervals in the booking link's timezone. At most 6 items. |
| `wed` | array of [object](#response-link-availability-wed) | No | Wednesday availability intervals in the booking link's timezone. At most 6 items. |
| `thu` | array of [object](#response-link-availability-thu) | No | Thursday availability intervals in the booking link's timezone. At most 6 items. |
| `fri` | array of [object](#response-link-availability-fri) | No | Friday availability intervals in the booking link's timezone. At most 6 items. |
| `sat` | array of [object](#response-link-availability-sat) | No | Saturday availability intervals in the booking link's timezone. At most 6 items. |
| `sun` | array of [object](#response-link-availability-sun) | No | Sunday availability intervals in the booking link's timezone. At most 6 items. |

### response.link.availability.mon[]

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `start` | string | Yes | Start of the interval. Pattern: `^([01]\d\|2[0-3]):[0-5]\d$`. |
| `end` | string | Yes | End of the interval; must be later than its start. Pattern: `^([01]\d\|2[0-3]):[0-5]\d$`. |

### response.link.availability.tue[]

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `start` | string | Yes | Start of the interval. Pattern: `^([01]\d\|2[0-3]):[0-5]\d$`. |
| `end` | string | Yes | End of the interval; must be later than its start. Pattern: `^([01]\d\|2[0-3]):[0-5]\d$`. |

### response.link.availability.wed[]

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `start` | string | Yes | Start of the interval. Pattern: `^([01]\d\|2[0-3]):[0-5]\d$`. |
| `end` | string | Yes | End of the interval; must be later than its start. Pattern: `^([01]\d\|2[0-3]):[0-5]\d$`. |

### response.link.availability.thu[]

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `start` | string | Yes | Start of the interval. Pattern: `^([01]\d\|2[0-3]):[0-5]\d$`. |
| `end` | string | Yes | End of the interval; must be later than its start. Pattern: `^([01]\d\|2[0-3]):[0-5]\d$`. |

### response.link.availability.fri[]

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `start` | string | Yes | Start of the interval. Pattern: `^([01]\d\|2[0-3]):[0-5]\d$`. |
| `end` | string | Yes | End of the interval; must be later than its start. Pattern: `^([01]\d\|2[0-3]):[0-5]\d$`. |

### response.link.availability.sat[]

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `start` | string | Yes | Start of the interval. Pattern: `^([01]\d\|2[0-3]):[0-5]\d$`. |
| `end` | string | Yes | End of the interval; must be later than its start. Pattern: `^([01]\d\|2[0-3]):[0-5]\d$`. |

### response.link.availability.sun[]

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `start` | string | Yes | Start of the interval. Pattern: `^([01]\d\|2[0-3]):[0-5]\d$`. |
| `end` | string | Yes | End of the interval; must be later than its start. Pattern: `^([01]\d\|2[0-3]):[0-5]\d$`. |

### response.link.questions[]

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | string | Yes | Stable question key you choose, unique within this link, such as project_goal. Reuse it when editing that question; no create-record call is needed. At least 1 character. At most 40 characters. |
| `label` | string | Yes | Text shown for this booking question or choice. At least 1 character. At most 200 characters. |
| `kind` | string | Yes | Question control: text is a short answer, textarea a longer answer, select a choice from options, and checkbox a yes/no response. Allowed: `"text"`, `"textarea"`, `"select"`, `"checkbox"`. |
| `required` | boolean | No | Whether the invitee must answer this booking question. |
| `options` | array of string | No | Choices for a select question; at least one is required when kind=select. Omit for other controls. At least 1 item. At most 12 items. |

## Errors & retries

Set `BRIGG_REQUEST_ID` once for each logical action. The samples send it as `Idempotency-Key`. Reuse the same value and body on retries, including after a timeout; a missing response does not prove the action failed.

For rate limits, follow the server’s retry guidance and back off. See [HTTP errors](https://developer.brigg.app/http#errors) and [safe retries and limits](https://developer.brigg.app/reliability).

| HTTP status | Code | What to do |
| --- | --- | --- |
| 400 | `invalid_json` | Send a JSON object with Content-Type: application/json. |
| 400 | `invalid_input` | Check the request fields and types. Supply a stable Idempotency-Key containing 1–200 characters. |
| 401 | `invalid_key` | The workspace API key is missing, invalid or revoked. |
| 403 | `forbidden` | Check the key’s operation and resource grants. Supply Brigg-Act-As with a member authorized by the key’s delegation grant. The selected member must be active and approved by the credential. This selects whose private data is used; the credential supplies permissions. Private ownership and project boundaries still apply. |
| 404 | `not_found` | The requested operation or resource is not available to this credential. |
| 429 | `rate_limited` | Wait for Retry-After before retrying. Reuse the original Idempotency-Key. |
| 409 | `needs_auth` | Connect a calendar and grant calendar write access in Brigg. |
| 422 | `invalid_timezone` | Use a valid IANA time zone. |

## Next steps

- [List booking links](https://developer.brigg.app/reference/meetings/ListBookingLinks)
- [List calendar connections](https://developer.brigg.app/reference/meetings/ListCalendarConnections)
- [Update booking link](https://developer.brigg.app/reference/meetings/UpdateBookingLink)
- [Cancel booking](https://developer.brigg.app/reference/meetings/CancelBooking)

[Download OpenAPI](https://developer.brigg.app/openapi.json) · [Versioning & retirement](https://developer.brigg.app/versioning)

## Examples

### Example request

Fictional values. Replace IDs and business details with values from your workspace.
#### cURL


```sh
curl 'https://api.brigg.app/v1/operations/meetings.CreateBookingLink' \
  -H "Authorization: Bearer $BRIGG_API_KEY" \
  -H 'Content-Type: application/json' \
  -H "Brigg-Act-As: $BRIGG_ACT_AS" \
  -H "Idempotency-Key: $BRIGG_REQUEST_ID" \
  --data-raw '{
  "name": "Project introduction",
  "duration_minutes": 30,
  "location_kind": "video",
  "timezone": "Europe/Oslo",
  "availability": {
    "mon": [
      {
        "start": "09:00",
        "end": "17:00"
      }
    ]
  }
}'
```

#### TypeScript


```typescript
const actAs = process.env.BRIGG_ACT_AS;
if (!actAs) throw new Error("Set BRIGG_ACT_AS to an approved member account ID.");

const requestId = process.env.BRIGG_REQUEST_ID;
if (!requestId) throw new Error("Set BRIGG_REQUEST_ID once per action and reuse it on retries.");

const response = await fetch("https://api.brigg.app/v1/operations/meetings.CreateBookingLink", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.BRIGG_API_KEY}`,
    "Content-Type": "application/json",
    "Brigg-Act-As": actAs,
    "Idempotency-Key": requestId, // Create once per action; reuse on retries.
  },
  body: JSON.stringify({
    "name": "Project introduction",
    "duration_minutes": 30,
    "location_kind": "video",
    "timezone": "Europe/Oslo",
    "availability": {
      "mon": [
        {
          "start": "09:00",
          "end": "17:00"
        }
      ]
    }
  }),
});
const result = await response.json();
if (!response.ok) throw new Error(`${response.status}: ${result.error} — ${result.message}`);
console.log(result);
```

#### Python


```python
import json
import os
from urllib.request import Request, urlopen
from urllib.error import HTTPError

request_id = os.environ["BRIGG_REQUEST_ID"]  # Reuse for retries of this action.
payload = {
  "name": "Project introduction",
  "duration_minutes": 30,
  "location_kind": "video",
  "timezone": "Europe/Oslo",
  "availability": {
    "mon": [
      {
        "start": "09:00",
        "end": "17:00"
      }
    ]
  }
}
request = Request(
    "https://api.brigg.app/v1/operations/meetings.CreateBookingLink",
    data=json.dumps(payload).encode("utf-8"),
    headers={
        "Authorization": f"Bearer {os.environ['BRIGG_API_KEY']}",
        "Content-Type": "application/json",
        "Brigg-Act-As": os.environ["BRIGG_ACT_AS"],
        "Idempotency-Key": request_id,  # Create once per action; reuse on retries.
    },
    method="POST",
)
try:
    with urlopen(request) as response:
        print(json.load(response))
except HTTPError as error:
    print(error.code, json.load(error))
    raise
```

#### Response example


```json
{
  "link": {
    "id": "bkl_example",
    "org_id": "o_example",
    "owner_account_id": "a_example",
    "slug": "project-introduction",
    "name": "Project introduction",
    "description": null,
    "duration_minutes": 30,
    "duration_options": null,
    "location_kind": "video",
    "location_detail": null,
    "timezone": "Europe/Oslo",
    "availability": {
      "mon": [
        {
          "start": "09:00",
          "end": "17:00"
        }
      ]
    },
    "window_days": 30,
    "min_notice_minutes": 240,
    "buffer_before_minutes": 0,
    "buffer_after_minutes": 0,
    "max_per_day": 0,
    "questions": null,
    "reminder_hours": 24,
    "collect_phone": false,
    "active": true,
    "created_at": 1794301200000,
    "updated_at": 1794301200000,
    "url": "https://example.com/book/project-introduction"
  }
}
```

## JSON schemas


```json-schema-request
{
  "type": "object",
  "properties": {
    "name": {
      "type": "string",
      "minLength": 1,
      "maxLength": 120,
      "description": "Meeting name displayed on the public booking page."
    },
    "description": {
      "type": "string",
      "maxLength": 2000,
      "description": "Introduction displayed to invitees. Send an empty string to clear it."
    },
    "duration_minutes": {
      "type": "integer",
      "minimum": 10,
      "maximum": 480,
      "default": 30,
      "description": "Fixed meeting length in minutes. Supplying this alone clears any previous choice of lengths. duration_options takes precedence when both are supplied."
    },
    "duration_options": {
      "type": "array",
      "items": {
        "type": "integer",
        "minimum": 10,
        "maximum": 480
      },
      "minItems": 1,
      "maxItems": 4,
      "description": "Offered lengths in minutes. One length makes a fixed-duration link; several let the invitee choose. The stored duration_minutes is the shortest offered length."
    },
    "location_kind": {
      "type": "string",
      "enum": [
        "video",
        "phone",
        "in_person",
        "custom"
      ],
      "default": "video",
      "description": "video creates a conference link; phone, in_person and custom use location_detail for instructions or an address."
    },
    "location_detail": {
      "type": "string",
      "maxLength": 500,
      "description": "Instructions for the chosen location, such as a phone number or street address. Send an empty string to clear them."
    },
    "timezone": {
      "type": "string",
      "default": "Europe/Oslo",
      "description": "An IANA time zone; refused invalid_timezone otherwise."
    },
    "availability": {
      "type": "object",
      "properties": {
        "mon": {
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "start": {
                "type": "string",
                "pattern": "^([01]\\d|2[0-3]):[0-5]\\d$",
                "description": "Start of the interval."
              },
              "end": {
                "type": "string",
                "pattern": "^([01]\\d|2[0-3]):[0-5]\\d$",
                "description": "End of the interval; must be later than its start."
              }
            },
            "required": [
              "start",
              "end"
            ],
            "additionalProperties": false
          },
          "maxItems": 6,
          "description": "Monday availability intervals in the booking link's timezone."
        },
        "tue": {
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "start": {
                "type": "string",
                "pattern": "^([01]\\d|2[0-3]):[0-5]\\d$",
                "description": "Start of the interval."
              },
              "end": {
                "type": "string",
                "pattern": "^([01]\\d|2[0-3]):[0-5]\\d$",
                "description": "End of the interval; must be later than its start."
              }
            },
            "required": [
              "start",
              "end"
            ],
            "additionalProperties": false
          },
          "maxItems": 6,
          "description": "Tuesday availability intervals in the booking link's timezone."
        },
        "wed": {
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "start": {
                "type": "string",
                "pattern": "^([01]\\d|2[0-3]):[0-5]\\d$",
                "description": "Start of the interval."
              },
              "end": {
                "type": "string",
                "pattern": "^([01]\\d|2[0-3]):[0-5]\\d$",
                "description": "End of the interval; must be later than its start."
              }
            },
            "required": [
              "start",
              "end"
            ],
            "additionalProperties": false
          },
          "maxItems": 6,
          "description": "Wednesday availability intervals in the booking link's timezone."
        },
        "thu": {
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "start": {
                "type": "string",
                "pattern": "^([01]\\d|2[0-3]):[0-5]\\d$",
                "description": "Start of the interval."
              },
              "end": {
                "type": "string",
                "pattern": "^([01]\\d|2[0-3]):[0-5]\\d$",
                "description": "End of the interval; must be later than its start."
              }
            },
            "required": [
              "start",
              "end"
            ],
            "additionalProperties": false
          },
          "maxItems": 6,
          "description": "Thursday availability intervals in the booking link's timezone."
        },
        "fri": {
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "start": {
                "type": "string",
                "pattern": "^([01]\\d|2[0-3]):[0-5]\\d$",
                "description": "Start of the interval."
              },
              "end": {
                "type": "string",
                "pattern": "^([01]\\d|2[0-3]):[0-5]\\d$",
                "description": "End of the interval; must be later than its start."
              }
            },
            "required": [
              "start",
              "end"
            ],
            "additionalProperties": false
          },
          "maxItems": 6,
          "description": "Friday availability intervals in the booking link's timezone."
        },
        "sat": {
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "start": {
                "type": "string",
                "pattern": "^([01]\\d|2[0-3]):[0-5]\\d$",
                "description": "Start of the interval."
              },
              "end": {
                "type": "string",
                "pattern": "^([01]\\d|2[0-3]):[0-5]\\d$",
                "description": "End of the interval; must be later than its start."
              }
            },
            "required": [
              "start",
              "end"
            ],
            "additionalProperties": false
          },
          "maxItems": 6,
          "description": "Saturday availability intervals in the booking link's timezone."
        },
        "sun": {
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "start": {
                "type": "string",
                "pattern": "^([01]\\d|2[0-3]):[0-5]\\d$",
                "description": "Start of the interval."
              },
              "end": {
                "type": "string",
                "pattern": "^([01]\\d|2[0-3]):[0-5]\\d$",
                "description": "End of the interval; must be later than its start."
              }
            },
            "required": [
              "start",
              "end"
            ],
            "additionalProperties": false
          },
          "maxItems": 6,
          "description": "Sunday availability intervals in the booking link's timezone."
        }
      },
      "additionalProperties": false,
      "description": "Complete weekly availability in the link's IANA timezone. Omitted or empty weekdays within this object are unavailable. This replaces the whole schedule, not only the listed days."
    },
    "window_days": {
      "type": "integer",
      "minimum": 1,
      "maximum": 90,
      "default": 30,
      "description": "Number of days into the future an invitee can book."
    },
    "min_notice_minutes": {
      "type": "integer",
      "minimum": 0,
      "maximum": 10080,
      "default": 240,
      "description": "Minimum notice required before a meeting starts, in minutes."
    },
    "buffer_before_minutes": {
      "type": "integer",
      "minimum": 0,
      "maximum": 240,
      "default": 0,
      "description": "Minutes reserved before each meeting."
    },
    "buffer_after_minutes": {
      "type": "integer",
      "minimum": 0,
      "maximum": 240,
      "default": 0,
      "description": "Minutes reserved after each meeting."
    },
    "max_per_day": {
      "type": "integer",
      "minimum": 0,
      "maximum": 50,
      "default": 0,
      "description": "Maximum bookings in a day. Zero means no daily limit."
    },
    "questions": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "minLength": 1,
            "maxLength": 40,
            "description": "Stable question key you choose, unique within this link, such as project_goal. Reuse it when editing that question; no create-record call is needed."
          },
          "label": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200,
            "description": "Text shown for this booking question or choice."
          },
          "kind": {
            "type": "string",
            "enum": [
              "text",
              "textarea",
              "select",
              "checkbox"
            ],
            "description": "Question control: text is a short answer, textarea a longer answer, select a choice from options, and checkbox a yes/no response."
          },
          "required": {
            "type": "boolean",
            "description": "Whether the invitee must answer this booking question."
          },
          "options": {
            "type": "array",
            "items": {
              "type": "string",
              "minLength": 1,
              "maxLength": 120
            },
            "minItems": 1,
            "maxItems": 12,
            "description": "Choices for a select question; at least one is required when kind=select. Omit for other controls."
          }
        },
        "required": [
          "id",
          "label",
          "kind"
        ],
        "additionalProperties": false
      },
      "maxItems": 5,
      "description": "Complete replacement question list. Send [] to remove all questions; omit the field on an update to preserve them."
    },
    "reminder_hours": {
      "type": "integer",
      "minimum": 0,
      "maximum": 336,
      "default": 24,
      "description": "Hours before a meeting to send a reminder. Zero disables reminders."
    },
    "collect_phone": {
      "type": "boolean",
      "default": false,
      "description": "Whether the booking form asks invitees for a phone number."
    },
    "active": {
      "type": "boolean",
      "default": true,
      "description": "Whether new bookings are accepted. False does not cancel confirmed appointments. Setting true requires the acting member's calendar write permission."
    }
  },
  "required": [
    "name"
  ],
  "additionalProperties": false,
  "examples": [
    {
      "name": "Project introduction",
      "duration_minutes": 30,
      "location_kind": "video",
      "timezone": "Europe/Oslo",
      "availability": {
        "mon": [
          {
            "start": "09:00",
            "end": "17:00"
          }
        ]
      }
    }
  ]
}
```


```json-schema-response
{
  "type": "object",
  "properties": {
    "link": {
      "description": "The link row plus its public url.",
      "title": "BookingLink",
      "type": "object",
      "properties": {
        "id": {
          "type": "string",
          "description": "Booking link ID. Use it to update or delete this link."
        },
        "org_id": {
          "type": "string",
          "description": "Workspace that owns the link."
        },
        "owner_account_id": {
          "type": "string",
          "description": "Member whose calendar supplies the available time slots."
        },
        "slug": {
          "type": "string",
          "description": "Unique URL segment for this link within the workspace."
        },
        "name": {
          "type": "string",
          "description": "Meeting name displayed to invitees."
        },
        "description": {
          "description": "Introduction displayed on the booking page.",
          "anyOf": [
            {
              "type": "string",
              "description": "Introduction displayed on the booking page."
            },
            {
              "type": "null"
            }
          ]
        },
        "duration_minutes": {
          "type": "integer",
          "description": "Default meeting length in minutes; the shortest length when several are offered."
        },
        "duration_options": {
          "description": "Offered meeting lengths in minutes. Null means a fixed duration_minutes.",
          "anyOf": [
            {
              "type": "array",
              "items": {
                "type": "integer"
              },
              "description": "Offered meeting lengths in minutes. Null means a fixed duration_minutes."
            },
            {
              "type": "null"
            }
          ]
        },
        "location_kind": {
          "type": "string",
          "enum": [
            "video",
            "phone",
            "in_person",
            "custom"
          ],
          "description": "video creates a conference link; phone, in_person and custom use location_detail for instructions or an address."
        },
        "location_detail": {
          "description": "Address, phone instructions or other location details.",
          "anyOf": [
            {
              "type": "string",
              "description": "Address, phone instructions or other location details."
            },
            {
              "type": "null"
            }
          ]
        },
        "timezone": {
          "type": "string",
          "description": "IANA time zone used to interpret the weekly availability, for example Europe/Oslo."
        },
        "availability": {
          "type": "object",
          "properties": {
            "mon": {
              "type": "array",
              "items": {
                "type": "object",
                "properties": {
                  "start": {
                    "type": "string",
                    "pattern": "^([01]\\d|2[0-3]):[0-5]\\d$",
                    "description": "Start of the interval."
                  },
                  "end": {
                    "type": "string",
                    "pattern": "^([01]\\d|2[0-3]):[0-5]\\d$",
                    "description": "End of the interval; must be later than its start."
                  }
                },
                "required": [
                  "start",
                  "end"
                ],
                "additionalProperties": false
              },
              "maxItems": 6,
              "description": "Monday availability intervals in the booking link's timezone."
            },
            "tue": {
              "type": "array",
              "items": {
                "type": "object",
                "properties": {
                  "start": {
                    "type": "string",
                    "pattern": "^([01]\\d|2[0-3]):[0-5]\\d$",
                    "description": "Start of the interval."
                  },
                  "end": {
                    "type": "string",
                    "pattern": "^([01]\\d|2[0-3]):[0-5]\\d$",
                    "description": "End of the interval; must be later than its start."
                  }
                },
                "required": [
                  "start",
                  "end"
                ],
                "additionalProperties": false
              },
              "maxItems": 6,
              "description": "Tuesday availability intervals in the booking link's timezone."
            },
            "wed": {
              "type": "array",
              "items": {
                "type": "object",
                "properties": {
                  "start": {
                    "type": "string",
                    "pattern": "^([01]\\d|2[0-3]):[0-5]\\d$",
                    "description": "Start of the interval."
                  },
                  "end": {
                    "type": "string",
                    "pattern": "^([01]\\d|2[0-3]):[0-5]\\d$",
                    "description": "End of the interval; must be later than its start."
                  }
                },
                "required": [
                  "start",
                  "end"
                ],
                "additionalProperties": false
              },
              "maxItems": 6,
              "description": "Wednesday availability intervals in the booking link's timezone."
            },
            "thu": {
              "type": "array",
              "items": {
                "type": "object",
                "properties": {
                  "start": {
                    "type": "string",
                    "pattern": "^([01]\\d|2[0-3]):[0-5]\\d$",
                    "description": "Start of the interval."
                  },
                  "end": {
                    "type": "string",
                    "pattern": "^([01]\\d|2[0-3]):[0-5]\\d$",
                    "description": "End of the interval; must be later than its start."
                  }
                },
                "required": [
                  "start",
                  "end"
                ],
                "additionalProperties": false
              },
              "maxItems": 6,
              "description": "Thursday availability intervals in the booking link's timezone."
            },
            "fri": {
              "type": "array",
              "items": {
                "type": "object",
                "properties": {
                  "start": {
                    "type": "string",
                    "pattern": "^([01]\\d|2[0-3]):[0-5]\\d$",
                    "description": "Start of the interval."
                  },
                  "end": {
                    "type": "string",
                    "pattern": "^([01]\\d|2[0-3]):[0-5]\\d$",
                    "description": "End of the interval; must be later than its start."
                  }
                },
                "required": [
                  "start",
                  "end"
                ],
                "additionalProperties": false
              },
              "maxItems": 6,
              "description": "Friday availability intervals in the booking link's timezone."
            },
            "sat": {
              "type": "array",
              "items": {
                "type": "object",
                "properties": {
                  "start": {
                    "type": "string",
                    "pattern": "^([01]\\d|2[0-3]):[0-5]\\d$",
                    "description": "Start of the interval."
                  },
                  "end": {
                    "type": "string",
                    "pattern": "^([01]\\d|2[0-3]):[0-5]\\d$",
                    "description": "End of the interval; must be later than its start."
                  }
                },
                "required": [
                  "start",
                  "end"
                ],
                "additionalProperties": false
              },
              "maxItems": 6,
              "description": "Saturday availability intervals in the booking link's timezone."
            },
            "sun": {
              "type": "array",
              "items": {
                "type": "object",
                "properties": {
                  "start": {
                    "type": "string",
                    "pattern": "^([01]\\d|2[0-3]):[0-5]\\d$",
                    "description": "Start of the interval."
                  },
                  "end": {
                    "type": "string",
                    "pattern": "^([01]\\d|2[0-3]):[0-5]\\d$",
                    "description": "End of the interval; must be later than its start."
                  }
                },
                "required": [
                  "start",
                  "end"
                ],
                "additionalProperties": false
              },
              "maxItems": 6,
              "description": "Sunday availability intervals in the booking link's timezone."
            }
          },
          "additionalProperties": false,
          "$schema": "http://json-schema.org/draft-07/schema#",
          "title": "WeeklyAvailability",
          "description": "Bookable local-time intervals by weekday. Missing or empty days are unavailable."
        },
        "window_days": {
          "type": "integer",
          "description": "How many days into the future invitees can book."
        },
        "min_notice_minutes": {
          "type": "integer",
          "description": "Minimum time before a meeting can start, in minutes."
        },
        "buffer_before_minutes": {
          "type": "integer",
          "description": "Minutes reserved before each meeting."
        },
        "buffer_after_minutes": {
          "type": "integer",
          "description": "Minutes reserved after each meeting."
        },
        "max_per_day": {
          "type": "integer",
          "description": "Maximum bookings per day. Zero means no daily limit."
        },
        "questions": {
          "description": "Additional questions shown to invitees. Null means no questions.",
          "anyOf": [
            {
              "type": "array",
              "items": {
                "type": "object",
                "properties": {
                  "id": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 40,
                    "description": "Stable question key you choose, unique within this link, such as project_goal. Reuse it when editing that question; no create-record call is needed."
                  },
                  "label": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 200,
                    "description": "Text shown for this booking question or choice."
                  },
                  "kind": {
                    "type": "string",
                    "enum": [
                      "text",
                      "textarea",
                      "select",
                      "checkbox"
                    ],
                    "description": "Question control: text is a short answer, textarea a longer answer, select a choice from options, and checkbox a yes/no response."
                  },
                  "required": {
                    "type": "boolean",
                    "description": "Whether the invitee must answer this booking question."
                  },
                  "options": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "minLength": 1,
                      "maxLength": 120
                    },
                    "minItems": 1,
                    "maxItems": 12,
                    "description": "Choices for a select question; at least one is required when kind=select. Omit for other controls."
                  }
                },
                "required": [
                  "id",
                  "label",
                  "kind"
                ],
                "additionalProperties": false
              },
              "maxItems": 5,
              "$schema": "http://json-schema.org/draft-07/schema#",
              "description": "Additional questions shown to invitees. Null means no questions."
            },
            {
              "type": "null"
            }
          ]
        },
        "reminder_hours": {
          "type": "integer",
          "description": "Hours before the meeting to send a reminder. Zero disables reminders."
        },
        "collect_phone": {
          "type": "boolean",
          "description": "Whether invitees are asked for a phone number."
        },
        "active": {
          "type": "boolean",
          "description": "Whether invitees can make new bookings through this link."
        },
        "created_at": {
          "type": "integer",
          "description": "Creation time in Unix milliseconds."
        },
        "updated_at": {
          "type": "integer",
          "description": "Last change in Unix milliseconds."
        },
        "url": {
          "type": "string",
          "description": "Public booking URL to share with invitees.",
          "format": "uri"
        }
      },
      "required": [
        "id",
        "org_id",
        "owner_account_id",
        "slug",
        "name",
        "description",
        "duration_minutes",
        "duration_options",
        "location_kind",
        "location_detail",
        "timezone",
        "availability",
        "window_days",
        "min_notice_minutes",
        "buffer_before_minutes",
        "buffer_after_minutes",
        "max_per_day",
        "questions",
        "reminder_hours",
        "collect_phone",
        "active",
        "created_at",
        "updated_at",
        "url"
      ],
      "additionalProperties": false
    }
  },
  "required": [
    "link"
  ],
  "additionalProperties": false,
  "examples": [
    {
      "link": {
        "id": "bkl_example",
        "org_id": "o_example",
        "owner_account_id": "a_example",
        "slug": "project-introduction",
        "name": "Project introduction",
        "description": null,
        "duration_minutes": 30,
        "duration_options": null,
        "location_kind": "video",
        "location_detail": null,
        "timezone": "Europe/Oslo",
        "availability": {
          "mon": [
            {
              "start": "09:00",
              "end": "17:00"
            }
          ]
        },
        "window_days": 30,
        "min_notice_minutes": 240,
        "buffer_before_minutes": 0,
        "buffer_after_minutes": 0,
        "max_per_day": 0,
        "questions": null,
        "reminder_hours": 24,
        "collect_phone": false,
        "active": true,
        "created_at": 1794301200000,
        "updated_at": 1794301200000,
        "url": "https://example.com/book/project-introduction"
      }
    }
  ]
}
```

