
# 预约

Appointments API 允许您为联系人预约您的事件类型，然后获取、列出、更新、取消或删除这些预约。它还可以回答大多数预约流程中首先出现的问题——哪些时间段确实有空——并涵盖日历方面的内容：列出您已连接的 Google 日历并导入其中已有的事件。当 Google 日历连接处于活动状态时，相应的日历事件会在后台自动创建并保持同步。使用 Zenchef 或 Formitable 作为其自身预订系统的餐厅也可以在此处进行验证和连接，以便 AI 代理预订真实的餐桌，而不是内部预约。

本页面上的所有路径均相对于基础 URL `https://api.youraiconnector.com/v1`。每个请求都需要您的 API 密钥——请参阅[身份验证](authentication.md)以获取发送密钥的完整方式列表。以下示例使用了 `X-API-Key` 标头，其中一个 cURL 示例也展示了 `?apiKey=` 查询形式。

> **事件与预约的区别：** *事件类型* 是可预订时段的定义（会议类型、时长、会议室）。*预约* 是针对特定联系人的事件类型的已预订实例。您可以通过引用联系人和事件类型来预订预约。

---

## 预约对象

每个返回预约的端点都使用相同的结构：

| 字段 | 描述 |
|---|---|
| `id` | 预约的唯一 ID。 |
| `contact_id` | 预约联系人的 ID。 |
| `event_id` | 预订该预约的事件类型 ID。 |
| `status` | `Confirmed` 或 `Canceled`。 |
| `start_time` | 预约开始时间，ISO 8601 UTC 格式。 |
| `end_time` | 预约结束时间，ISO 8601 UTC 格式。 |
| `created_at` | 预约创建时间。 |
| `last_modified_at` | 预约最后修改时间。 |
| `room_name` | 当事件类型使用会议室时，预订该预约的会议室或资源。 |
| `description` | 预约的自由格式描述。 |
| `summary` | 简短摘要或标题。 |
| `cancelation_reason` | 取消预约时提供的理由（如有）。 |
| `google_calendar_event_id` | 关联的 Google 日历事件 ID。在日历同步完成后设置；当未连接日历或同步仍在进行时为 `null`。 |
| `calendar_synced` | 当预约关联到日历事件时为 `true`。 |
| `imported` | 当预约是从外部日历导入而非直接预订时为 `true`。 |
| `is_recurring` | 当预约属于循环系列时为 `true`。 |
| `recurrence_frequency` | 循环预约的重复频率。 |
| `recurring_event_id` | 该预约所属的循环系列 ID。 |
| `recurring_interval` | 循环预约的重复间隔。 |
| `recurring_sequence` | 该预约在循环系列中的位置。 |
| `end_after_x_occurrences` | 循环系列结束前的发生次数。 |
| `booking_provider` | 当通过已连接的预订提供商预订时，预订来源系统。 |

> **关于日历同步：** 在您预订或更改预约后，`google_calendar_event_id` 可能仍为 `null`，且 `calendar_synced` 可能为 `false`，因为同步是在稍后的后台运行的。请在稍后重新获取该预约以查看已填充的日历字段。

---

## 查找可用时段

`GET /appointments/available-slots`

返回在两个时间点之间，某个事件类型确实空闲的时间段。这通常是预约流程中的**第一步**调用：显示这些时段，让用户选择一个，然后将选定的时间发布到 [预约](#book-an-appointment)。

该结果已经考虑了事件类型本身的开放时间和时段长度、其房间、您已在该类型上预订的预约，以及连接的 Google 日历上所有被占用的时间——因此，此处返回的时段是您可以预订的时段。

| 查询参数 | 必填 | 描述 |
|---|---|---|
| `event_id` | 是 | 要检查的事件类型。必须属于您的账户。 |
| `start_time` | 是 | 您想要查询时段的时间窗口开始时间，ISO 8601 日期时间格式。 |
| `end_time` | 是 | 时间窗口的结束时间，ISO 8601 日期时间格式。包含整个结束日期。 |

结果按天分组返回——当事件类型使用房间时，按每天每个房间进行分组：

| 字段 | 描述 |
|---|---|
| `date` | 该组涵盖的日期，格式为 `DD/MM/YYYY`。 |
| `day` | 小写的星期几名称，例如 `monday`。 |
| `room_name` | 当事件类型使用房间时，该组所属的房间或资源。 |
| `available_slots` | 当天可预订的时间块，按时间先后顺序排列。 |

`available_slots` 中的每个条目包含：

| 字段 | 描述 |
|---|---|
| `start_time` | 时间块开始时间，格式为 `HH:mm`。 |
| `end_time` | 时间块结束时间，格式为 `HH:mm`。 |
| `available` | `true` ——仅返回空闲时间。 |
| `spots_left` | 此时间块中还能容纳多少个预约。仅在每个时段接受多个预约的事件类型中出现。 |

> **时间是事件类型的本地时间，而非 UTC 时间。** `date`、`start_time` 和 `end_time` 是事件类型所在时区（其覆盖设置，或在没有覆盖设置时为您账户的时区）的挂钟时间值。[预约](#book-an-appointment) 需要一个 ISO 8601 UTC 即时时间，因此请在发布之前转换您选择的时段。

**cURL**

```bash
curl "https://api.youraiconnector.com/v1/appointments/available-slots?event_id=event_xyz789&start_time=2026-06-15T00:00:00.000Z&end_time=2026-06-19T00:00:00.000Z" \
  -H "X-API-Key: YOUR_API_KEY"
```

**JavaScript**

```javascript
const params = new URLSearchParams({
  event_id: "event_xyz789",
  start_time: "2026-06-15T00:00:00.000Z",
  end_time: "2026-06-19T00:00:00.000Z",
});
const res = await fetch(
  `https://api.youraiconnector.com/v1/appointments/available-slots?${params}`,
  { headers: { "X-API-Key": "YOUR_API_KEY" } }
);
const data = await res.json();
console.log(data.data);
```

**Python**

```python
import requests

res = requests.get(
    "https://api.youraiconnector.com/v1/appointments/available-slots",
    headers={"X-API-Key": "YOUR_API_KEY"},
    params={
        "event_id": "event_xyz789",
        "start_time": "2026-06-15T00:00:00.000Z",
        "end_time": "2026-06-19T00:00:00.000Z",
    },
)
print(res.json()["data"])
```

**响应** (`200 OK`)：

```json
{
  "success": true,
  "data": [
    {
      "date": "15/06/2026",
      "day": "monday",
      "room_name": "Room A",
      "available_slots": [
        { "start_time": "10:00", "end_time": "10:30", "available": true },
        { "start_time": "10:30", "end_time": "11:00", "available": true }
      ]
    },
    {
      "date": "16/06/2026",
      "day": "tuesday",
      "room_name": "Room A",
      "available_slots": [
        { "start_time": "09:00", "end_time": "09:30", "available": true, "spots_left": 2 }
      ]
    }
  ]
}
```

没有空闲时间的一天将不会显示。缺少 `event_id`、`start_time` 或 `end_time` 会返回 `400`；不属于您账户的事件类型会返回 `404`。

---

## 预订预约

`POST /appointments`

为联系人预订特定事件类型的新预约。结束时间会根据事件类型的时段时长自动计算。

预订会进行冲突检查：如果请求的时段与同一事件类型上现有的已确认预约重叠，请求将失败并返回 `409`，且不会创建任何内容。

| 字段 | 必填 | 描述 |
|---|---|---|
| `contact_id` | 是 | 预订联系人的 ID。必须属于您的账户。 |
| `event_id` | 是 | 预订的事件类型 ID。必须属于您的账户。 |
| `start_time` | 是 | 所需的开始时间，ISO 8601 日期时间格式。 |
| `room_name` | 否 | 当事件类型使用会议室时，会议室或资源名称。 |

**cURL**（使用 `?apiKey=` 查询表单）

```bash
curl -X POST "https://api.youraiconnector.com/v1/appointments?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "contact_id": "contact_abc123",
    "event_id": "event_xyz789",
    "start_time": "2026-06-15T10:00:00.000Z",
    "room_name": "Room A"
  }'
```

**JavaScript**

```javascript
const res = await fetch("https://api.youraiconnector.com/v1/appointments", {
  method: "POST",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    contact_id: "contact_abc123",
    event_id: "event_xyz789",
    start_time: "2026-06-15T10:00:00.000Z",
    room_name: "Room A",
  }),
});
const data = await res.json();
console.log(data.appointment_id);
```

**Python**

```python
import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/appointments",
    headers={
        "X-API-Key": "YOUR_API_KEY",
        "Content-Type": "application/json",
    },
    json={
        "contact_id": "contact_abc123",
        "event_id": "event_xyz789",
        "start_time": "2026-06-15T10:00:00.000Z",
        "room_name": "Room A",
    },
)
print(res.json()["appointment_id"])
```

**响应** (`201 Created`)：

```json
{
  "success": true,
  "appointment_id": "aBcD1234eFgH5678",
  "appointment": {
    "id": "aBcD1234eFgH5678",
    "contact_id": "contact_abc123",
    "event_id": "event_xyz789",
    "status": "Confirmed",
    "start_time": "2026-06-15T10:00:00.000Z",
    "end_time": "2026-06-15T10:30:00.000Z",
    "created_at": "2026-06-10T09:00:00.000Z",
    "last_modified_at": "2026-06-10T09:00:00.000Z",
    "room_name": "Room A",
    "google_calendar_event_id": null,
    "calendar_synced": false
  }
}
```

---

## 获取预约

`GET /appointments/{appointmentId}`

根据 ID 返回单个预约，包括其日历同步状态。

**cURL**

```bash
curl "https://api.youraiconnector.com/v1/appointments/aBcD1234eFgH5678" \
  -H "X-API-Key: YOUR_API_KEY"
```

**JavaScript**

```javascript
const res = await fetch(
  "https://api.youraiconnector.com/v1/appointments/aBcD1234eFgH5678",
  { headers: { "X-API-Key": "YOUR_API_KEY" } }
);
const data = await res.json();
console.log(data.appointment);
```

**Python**

```python
import requests

res = requests.get(
    "https://api.youraiconnector.com/v1/appointments/aBcD1234eFgH5678",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
print(res.json()["appointment"])
```

**响应** (`200 OK`)：

```json
{
  "success": true,
  "appointment": {
    "id": "aBcD1234eFgH5678",
    "contact_id": "contact_abc123",
    "event_id": "event_xyz789",
    "status": "Confirmed",
    "start_time": "2026-06-15T10:00:00.000Z",
    "end_time": "2026-06-15T10:30:00.000Z",
    "room_name": "Room A",
    "google_calendar_event_id": "abc123googleevent",
    "calendar_synced": true
  }
}
```

---

## 列出预约

`GET /appointments`

列出您账户下的预约，按最新时间排序，并使用基于游标的分页。

| 查询参数 | 必填 | 描述 |
|---|---|---|
| `contact_id` | 否 | 仅返回此联系人的预约。按联系人筛选的列表**仅包含已确认的预约**。 |
| `date` | 否 | 仅返回此日历日的预约 (`YYYY-MM-DD`)。**需要 `contact_id`。** |
| `status` | 否 | 按 `Confirmed` 或 `Canceled` 筛选。仅在**没有** `contact_id` 时可用。 |
| `limit` | 否 | 每页大小，1 到 100 之间的整数。默认为 `50`。 |
| `cursor` | 否 | 上次响应中的 `next_cursor` 值。 |

需要注意的几条规则：

- **无筛选条件时**，您将逐页获取账户下的所有预约。
- **按联系人** — 设置 `contact_id` 以查看某位联系人的已确认预约。您也可以通过同时传递 `date` 将范围缩小到特定的一天。
- **按状态** — 设置 `status`（不带 `contact_id`）以列出账户中仅 `Confirmed` 或仅 `Canceled` 的预约。
- 在没有 `contact_id` 的情况下使用 `date` 筛选，或将 `status=Canceled` 与 `contact_id` 一起使用，将返回 `400`。

**cURL**

```bash
curl "https://api.youraiconnector.com/v1/appointments?contact_id=contact_abc123&date=2026-06-15" \
  -H "X-API-Key: YOUR_API_KEY"
```

**JavaScript**

```javascript
const params = new URLSearchParams({
  contact_id: "contact_abc123",
  date: "2026-06-15",
});
const res = await fetch(
  `https://api.youraiconnector.com/v1/appointments?${params}`,
  { headers: { "X-API-Key": "YOUR_API_KEY" } }
);
const data = await res.json();
console.log(data.appointments, data.next_cursor);
```

**Python**

```python
import requests

res = requests.get(
    "https://api.youraiconnector.com/v1/appointments",
    headers={"X-API-Key": "YOUR_API_KEY"},
    params={"contact_id": "contact_abc123", "date": "2026-06-15"},
)
data = res.json()
print(data["appointments"], data["next_cursor"])
```

**响应** (`200 OK`)：

```json
{
  "success": true,
  "appointments": [
    {
      "id": "aBcD1234eFgH5678",
      "contact_id": "contact_abc123",
      "event_id": "event_xyz789",
      "status": "Confirmed",
      "start_time": "2026-06-15T10:00:00.000Z",
      "end_time": "2026-06-15T10:30:00.000Z",
      "calendar_synced": true
    }
  ],
  "next_cursor": null
}
```

要翻阅结果，请将上一次响应中的 `next_cursor` 作为下一次请求的 `cursor` 传递。持续此操作直到 `next_cursor` 为 `null`。请参阅 [错误与分页](errors-and-pagination.md) 以了解共享的分页模式。

---

## 更新预约

`PUT /appointments/{appointmentId}`

重新安排预约或更改其详细信息。仅发送您想要更改的字段 — 至少需要一个字段。合并后的开始和结束时间必须保持时间顺序（`end_time` 必须在 `start_time` 之后）。更改会自动同步到关联的日历事件。

| 字段 | 描述 |
|---|---|
| `start_time` | 新的开始时间，ISO 8601 日期时间格式。 |
| `end_time` | 新的结束时间，ISO 8601 日期时间格式。必须晚于开始时间。 |
| `room_name` | 新的房间或资源名称。 |
| `description` | 新的描述，或使用 `null` 清除它。 |
| `summary` | 新的摘要，或使用 `null` 清除它。 |

**cURL**

```bash
curl -X PUT "https://api.youraiconnector.com/v1/appointments/aBcD1234eFgH5678" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "start_time": "2026-06-16T10:00:00.000Z",
    "end_time": "2026-06-16T10:30:00.000Z"
  }'
```

**JavaScript**

```javascript
const res = await fetch(
  "https://api.youraiconnector.com/v1/appointments/aBcD1234eFgH5678",
  {
    method: "PUT",
    headers: {
      "X-API-Key": "YOUR_API_KEY",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      start_time: "2026-06-16T10:00:00.000Z",
      end_time: "2026-06-16T10:30:00.000Z",
    }),
  }
);
const data = await res.json();
console.log(data.appointment);
```

**Python**

```python
import requests

res = requests.put(
    "https://api.youraiconnector.com/v1/appointments/aBcD1234eFgH5678",
    headers={
        "X-API-Key": "YOUR_API_KEY",
        "Content-Type": "application/json",
    },
    json={
        "start_time": "2026-06-16T10:00:00.000Z",
        "end_time": "2026-06-16T10:30:00.000Z",
    },
)
print(res.json()["appointment"])
```

**响应** (`200 OK`)：

```json
{
  "success": true,
  "appointment_id": "aBcD1234eFgH5678",
  "appointment": {
    "id": "aBcD1234eFgH5678",
    "contact_id": "contact_abc123",
    "event_id": "event_xyz789",
    "status": "Confirmed",
    "start_time": "2026-06-16T10:00:00.000Z",
    "end_time": "2026-06-16T10:30:00.000Z",
    "calendar_synced": true
  }
}
```

---

## 取消预约

`POST /appointments/{appointmentId}/cancel`

取消已确认的预约，并可选择记录原因。预约将保留在您的账户中，状态为 `Canceled`，关联的日历事件会在后台自动移除。取消一个已取消的预约将返回 `400`。

| 字段 | 必填 | 描述 |
|---|---|---|
| `cancellation_reason` | 否 | 取消原因，将存储在预约记录中。 |

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/appointments/aBcD1234eFgH5678/cancel" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "cancellation_reason": "Client asked to reschedule next month"
  }'
```

**JavaScript**

```javascript
const res = await fetch(
  "https://api.youraiconnector.com/v1/appointments/aBcD1234eFgH5678/cancel",
  {
    method: "POST",
    headers: {
      "X-API-Key": "YOUR_API_KEY",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      cancellation_reason: "Client asked to reschedule next month",
    }),
  }
);
const data = await res.json();
console.log(data.success);
```

**Python**

```python
import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/appointments/aBcD1234eFgH5678/cancel",
    headers={
        "X-API-Key": "YOUR_API_KEY",
        "Content-Type": "application/json",
    },
    json={"cancellation_reason": "Client asked to reschedule next month"},
)
print(res.json()["success"])
```

**响应** (`200 OK`)：

```json
{
  "success": true,
  "appointment_id": "aBcD1234eFgH5678"
}
```

---

## 删除预约

`DELETE /appointments/{appointmentId}`

永久删除预约及其引用。如果您只想取消预订但保留记录，请改用 [取消](#cancel-an-appointment)。

**cURL**

```bash
curl -X DELETE "https://api.youraiconnector.com/v1/appointments/aBcD1234eFgH5678" \
  -H "X-API-Key: YOUR_API_KEY"
```

**JavaScript**

```javascript
const res = await fetch(
  "https://api.youraiconnector.com/v1/appointments/aBcD1234eFgH5678",
  { method: "DELETE", headers: { "X-API-Key": "YOUR_API_KEY" } }
);
const data = await res.json();
console.log(data.success);
```

**Python**

```python
import requests

res = requests.delete(
    "https://api.youraiconnector.com/v1/appointments/aBcD1234eFgH5678",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
print(res.json()["success"])
```

**响应** (`200 OK`)：

```json
{
  "success": true
}
```

---

## 列出您已连接的 Google 日历

`GET /appointments/google-calendars`

直接从 Google 返回此账户上可用的 Google 日历——这对于向账户所有者显示一个选择器以决定从下方导入哪个日历，或者仅仅是为了确认连接是否处于活动状态非常有用。

只有在账户连接了 Google 日历（设置 → 集成）且至少拥有读取权限后，此功能才有效。如果尚未连接，或者授予的权限不再包含日历读取范围，您将收到一个 `400`，提示您（重新）连接它。

**cURL**

```bash
curl "https://api.youraiconnector.com/v1/appointments/google-calendars" \
  -H "X-API-Key: YOUR_API_KEY"
```

**JavaScript**

```javascript
const res = await fetch("https://api.youraiconnector.com/v1/appointments/google-calendars", {
  headers: { "X-API-Key": "YOUR_API_KEY" },
});
const data = await res.json();
console.log(data.data);
```

**Python**

```python
import requests

res = requests.get(
    "https://api.youraiconnector.com/v1/appointments/google-calendars",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
print(res.json()["data"])
```

**响应** (`200 OK`)：

```json
{
  "success": true,
  "data": [
    {
      "id": "primary",
      "summary": "jane@example.com",
      "timeZone": "America/New_York",
      "accessRole": "owner",
      "primary": true
    },
    {
      "id": "abcdefg1234567890@group.calendar.google.com",
      "summary": "Bookings",
      "timeZone": "America/New_York",
      "accessRole": "writer"
    }
  ]
}
```

每个条目都是 Google 自己的 [`CalendarListEntry`](https://developers.google.com/calendar/api/v3/reference/calendarList) 格式，因此字段名称遵循 Google 的 `camelCase`，而不是本 API 通常使用的 `snake_case` —— 这是原样传递的 Google 数据，而非我们的数据。如果连接缺失或已撤销，将返回 `400`，并附带说明需要（重新）连接 Google 日历的错误信息。

---

## 从 Google 日历导入事件

`POST /appointments/import-calendar-events`

拉取已存在于营销活动或 AI 代理关联的 Google 日历中的事件，并将其转换为预约 —— 这在您首次连接已有预订的日历时非常有用。此过程可能需要一些时间（每个事件都会经过提取以确定其归属），因此它从不内联运行：请求会将任务放入后台队列，并返回一个 `job_id` 供您轮询。

| 字段 | 必填 | 描述 |
|---|---|---|
| `campaign_id` | 二选一 | 要从中导入关联日历的营销活动。 |
| `agent_id` | 二选一 | 要从中导入关联日历的 AI 代理。 |
| `identifier` | 是 | `"EMAIL"` 或 `"PHONE_NUMBER"` —— 从每个日历事件中提取哪种联系信息，以匹配或创建其所属的联系人。 |

发送 `campaign_id` / `agent_id` 中的且仅限其中一个，不能两个都发，也不能一个都不发 —— 任何其他组合都会返回 `400`。您发送的任何一个都必须属于您的账户，否则您将收到 `404`。

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/appointments/import-calendar-events?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "agent_id": "agent_abc123",
    "identifier": "EMAIL"
  }'
```

**JavaScript**

```javascript
const res = await fetch("https://api.youraiconnector.com/v1/appointments/import-calendar-events", {
  method: "POST",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    agent_id: "agent_abc123",
    identifier: "EMAIL",
  }),
});
const data = await res.json();
console.log(data.job_id);
```

**Python**

```python
import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/appointments/import-calendar-events",
    headers={
        "X-API-Key": "YOUR_API_KEY",
        "Content-Type": "application/json",
    },
    json={"agent_id": "agent_abc123", "identifier": "EMAIL"},
)
print(res.json()["job_id"])
```

**响应** (`202 Accepted`)：

```json
{
  "success": true,
  "job_id": "jK9mQ2xR7pL4wN1t",
  "status": "queued",
  "campaign_id": null,
  "agent_id": "agent_abc123"
}
```

`campaign_id` 和 `agent_id` 会回显您发送的那个字段；另一个始终为 `null`。

### 轮询导入任务

`GET /appointments/import-calendar-events/{jobId}`

```bash
curl "https://api.youraiconnector.com/v1/appointments/import-calendar-events/jK9mQ2xR7pL4wN1t" \
  -H "X-API-Key: YOUR_API_KEY"
```

**响应** (`200 OK`)：

```json
{
  "success": true,
  "job_id": "jK9mQ2xR7pL4wN1t",
  "status": "completed",
  "message": "Imported 12 events as appointments.",
  "error": null
}
```

| `status` | 含义 |
|---|---|
| `queued` | 尚未处理。请继续轮询。 |
| `processing` | 导入正在运行。请继续轮询。 |
| `completed` | 已完成 —— `message` 包含简短的人类可读摘要。 |
| `failed` | 出错了 —— `error` 包含原因。 |

对不存在（或属于其他账户）的 `jobId` 执行 `GET` 会返回 `404`。

---

## 餐厅预订集成（Zenchef / Formitable）

Zenchef 和 Formitable 是餐厅预订系统，您的 AI 代理可以通过它们预订真实的餐位。每个系统都有一个**公开、无需身份验证的预订小部件**（`https://api.youraiconnector.com/v1/zenchef-widget/...` 和 `https://api.youraiconnector.com/v1/formitable-widget/...`），可在聊天中为用餐者呈现 —— 这些小部件路由是旨在浏览器中打开的纯 HTML 页面，而非 JSON API 端点，因此此处不予记录。以下是账户管理端点：验证餐厅 ID 是否属于账户持有者，然后对其进行添加、更新或移除。

### Zenchef

连接 Zenchef 餐厅需要进行两步验证，以确保账户持有人在将其接入机器人之前证明他们确实经营该餐厅：首先检查 ID 是否存在（不显示名称），然后让他们自行输入餐厅名称并验证其是否匹配。

**第 1 步 — 检查餐厅 ID 是否存在**

`POST /appointments/zenchef-restaurants/check`

| 字段 | 必填 | 描述 |
|---|---|---|
| `restaurant_id` | 是 | 要检查的 Zenchef 餐厅 ID。 |

```bash
curl -X POST "https://api.youraiconnector.com/v1/appointments/zenchef-restaurants/check?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "restaurant_id": "12345" }'
```

**响应** (`200 OK`)：

```json
{
  "success": true,
  "data": { "exists": true, "requiresNameVerification": true }
}
```

`exists: false` 表示没有 Zenchef 餐厅拥有该 ID — 无需进行其他操作。每个账户每 5 分钟限额 10 次检查；超出限制将返回 `429`。

**第 2 步 — 验证餐厅名称**

`POST /appointments/zenchef-restaurants/verify-name`

| 字段 | 必填 | 描述 |
|---|---|---|
| `restaurant_id` | 是 | 来自第 1 步的 Zenchef 餐厅 ID。 |
| `user_input_name` | 是 | 账户持有人输入的名称 — 与 Zenchef 上餐厅的真实名称进行比对（忽略大小写/空格）。 |

```bash
curl -X POST "https://api.youraiconnector.com/v1/appointments/zenchef-restaurants/verify-name?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "restaurant_id": "12345", "user_input_name": "The Blue Door Bistro" }'
```

**响应** (`200 OK`)：

```json
{
  "success": true,
  "data": {
    "verified": true,
    "restaurantDetails": {
      "id": "12345",
      "name": "The Blue Door Bistro",
      "address": "1 Rue de Rivoli, Paris",
      "status": "active"
    }
  }
}
```

`verified: false` 表示名称不匹配 — `restaurantDetails` 被省略，请要求账户持有人重试。每 5 分钟限额 3 次尝试（比存在性检查更严格，因为这是实际的验证步骤）。如果 `restaurant_id` 在 Zenchef 上不再解析，则返回 `404`。

**第 3 步 — 保存餐厅**

`POST /appointments/zenchef-restaurants`

| 字段 | 必填 | 描述 |
|---|---|---|
| `restaurant_id` | 是 | 1–64 个字符，字母/数字/下划线/连字符。 |
| `restaurant_name` | 是 | 来自第 2 步的已验证餐厅名称。 |

```bash
curl -X POST "https://api.youraiconnector.com/v1/appointments/zenchef-restaurants?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "restaurant_id": "12345", "restaurant_name": "The Blue Door Bistro" }'
```

**响应** (`201 Created`)：

```json
{ "success": true, "data": { "restaurantId": "12345" } }
```

**更新已保存的 Zenchef 餐厅**

`PUT /appointments/zenchef-restaurants/{restaurantId}`

| 字段 | 必填 | 说明 |
|---|---|---|
| `restaurant_name` | 否 | 新的显示名称。 |
| `is_active` | 否 | 设置 `false` 以阻止机器人预订该餐厅，而无需将其删除。 |

```bash
curl -X PUT "https://api.youraiconnector.com/v1/appointments/zenchef-restaurants/12345" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "is_active": false }'
```

**响应** (`200 OK`)：与上述保存响应的格式相同。

**移除 Zenchef 餐厅**

`DELETE /appointments/zenchef-restaurants/{restaurantId}`

```bash
curl -X DELETE "https://api.youraiconnector.com/v1/appointments/zenchef-restaurants/12345" \
  -H "X-API-Key: YOUR_API_KEY"
```

**响应** (`200 OK`)：`{ "success": true, "data": { "restaurantId": "12345" } }`

如果 `restaurantId` 当前不在账户中，更新或删除时将返回 `404`。

### Formitable

Formitable 不需要像 Zenchef 那样进行两步名称验证——它的餐厅 ID 已经在每个企业范围内确定，因此一次验证调用就足够了。它还有一个详细信息查询功能，用于在设置期间缓存餐厅的网站 URL。

**验证餐厅 ID**

`POST /appointments/formitable-restaurants/verify`

| 字段 | 必填 | 说明 |
|---|---|---|
| `restaurant_id` | 是 | Formitable 餐厅 ID。 |
| `language` | 否 | 探测请求的语言标签。默认为 `"nl"`。 |

```bash
curl -X POST "https://api.youraiconnector.com/v1/appointments/formitable-restaurants/verify?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "restaurant_id": "the-blue-door", "language": "en" }'
```

**响应** (`200 OK`)：

```json
{
  "success": true,
  "data": {
    "verified": true,
    "restaurantDetails": {
      "restaurantId": "the-blue-door",
      "productCount": 4,
      "sampleProductTitle": "Dinner for two",
      "language": "en"
    }
  }
}
```

如果 Formitable 无法识别 `restaurant_id`，则返回 `404`。每个账户每 5 分钟限流 10 次尝试。

**获取餐厅详细信息**

`GET /appointments/formitable-restaurants/{restaurantId}/details?language=en`

从 Formitable 获取餐厅的公开资料，包括其网站——用于在设置餐厅时缓存网站 URL。`language` 是一个可选的查询参数，默认为 `"en"`。

```bash
curl "https://api.youraiconnector.com/v1/appointments/formitable-restaurants/the-blue-door/details?language=en" \
  -H "X-API-Key: YOUR_API_KEY"
```

**响应** (`200 OK`)：

```json
{
  "success": true,
  "data": {
    "uid": "the-blue-door",
    "name": "The Blue Door Bistro",
    "website": "https://thebluedoorbistro.com",
    "email": "info@thebluedoorbistro.com",
    "telephone": "+31201234567",
    "streetAddress": "Prinsengracht 1",
    "zipcode": "1015 AB",
    "city": "Amsterdam",
    "country": "Netherlands",
    "countryCode": "NL",
    "currency": "EUR"
  }
}
```

**保存餐厅**

`POST /appointments/formitable-restaurants`

| 字段 | 必填 | 描述 |
|---|---|---|
| `restaurant_id` | 是 | 1–64 个字符，包含字母/数字/下划线/连字符。 |
| `restaurant_name` | 是 | 显示名称。 |
| `language` | 是 | ISO 语言标签，例如 `"en"` 或 `"en-GB"`。 |
| `website_url` | 否 | 餐厅网站，来自上述详情查询。必须是 `http(s)://`。 |

```bash
curl -X POST "https://api.youraiconnector.com/v1/appointments/formitable-restaurants?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "restaurant_id": "the-blue-door",
    "restaurant_name": "The Blue Door Bistro",
    "language": "en",
    "website_url": "https://thebluedoorbistro.com"
  }'
```

**响应** (`201 Created`)：`{ "success": true, "data": { "restaurantId": "the-blue-door" } }`

**更新已保存的 Formitable 餐厅**

`PUT /appointments/formitable-restaurants/{restaurantId}`

| 字段 | 必填 | 描述 |
|---|---|---|
| `restaurant_name` | 否 | 新的显示名称。 |
| `language` | 否 | 新的 ISO 语言标签。 |
| `is_active` | 否 | 设置为 `false` 可阻止机器人预订该餐厅，而无需将其移除。 |
| `website_url` | 否 | 新的网站 URL。 |

```bash
curl -X PUT "https://api.youraiconnector.com/v1/appointments/formitable-restaurants/the-blue-door" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "is_active": false }'
```

**响应** (`200 OK`)：与上述保存响应的格式相同。

**移除 Formitable 餐厅**

`DELETE /appointments/formitable-restaurants/{restaurantId}`

```bash
curl -X DELETE "https://api.youraiconnector.com/v1/appointments/formitable-restaurants/the-blue-door" \
  -H "X-API-Key: YOUR_API_KEY"
```

**响应** (`200 OK`)：`{ "success": true, "data": { "restaurantId": "the-blue-door" } }`

如果 `restaurantId` 当前不在账户中，更新或删除时将返回 `404`。

> **所有 Zenchef/Formitable 端点的错误格式：** 与本页其余部分不同，此处的错误状态会携带两次——一次作为 HTTP 状态，另一次作为主体中的 `error_code`——例如 `{ "success": false, "error": "Restaurant not found", "error_code": 404 }`。处理方式与其他错误相同：检查 `success`，读取 `error` 以获取消息。

---

## 预约 API 错误

预约端点返回标准的错误封装：

```json
{
  "success": false,
  "error": "Appointment not found"
}
```

| 状态码 | 在预约端点上发生的情况 |
|---|---|
| `400` | 缺少必填字段或字段无效 —— 例如错误的 `start_time`、`end_time` 不在 `start_time` 之后、无效的筛选器组合、没有要更新的字段，或预约已取消。 |
| `404` | 未找到预约、联系人或事件类型。 |
| `409` | 请求的时间段已被占用（预订冲突）。 |

每个端点都可能返回的共享代码 — `401`, `403`（您的套餐不包含 API 访问权限）, `429`（速率限制）和 `500` — 及其重试指南列在 [错误与分页](errors-and-pagination.md) 中。

---

## 后续步骤

- [联系人](contacts.md) — 创建并查找您为其预订的联系人。
- [消息与对话](messages.md) — 向联系人发送确认或提醒。
- [Webhooks](webhooks.md) — 在预约发生变更时获取通知。
