预约
Appointments API 允许您为联系人预约您的活动类型,然后获取、列出、更新、取消或删除这些预约。它还回答了大多数预订流程中首先出现的问题——哪些时间段确实有空——并涵盖了日历方面的内容:列出您已连接的 Google 日历并导入其中已有的事件。当 Google 日历连接处于活动状态时,相应的日历事件会自动在后台创建并保持同步。使用 Zenchef、Formitable、OpenTable 或 TheFork 作为其自身预订系统的餐厅也可以在此处进行验证和连接,以便 AI 代理预订真实的餐位,而不是内部预约——在 Trafft 中管理日程的预约业务也可以通过同样的方式进行连接。
本页面上的所有路径均相对于基础 URL https://api.dmchamp.com/v1。每个请求都需要您的 API 密钥——请参阅身份验证以获取发送密钥的完整方式列表。以下示例使用了 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
返回在两个时间点之间,某个事件类型确实空闲的时间段。这通常是预约流程中的第一步调用:显示这些时段,让用户选择一个,然后将选定的时间发布到 预约。
该结果已经考虑了事件类型本身的开放时间和时段长度、其房间、您已在该类型上预订的预约,以及连接的 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是事件类型所在时区(其覆盖设置,或在没有覆盖设置时为您账户的时区)的挂钟时间值。预约 需要一个 ISO 8601 UTC 即时时间,因此请在发布之前转换您选择的时段。
cURL
curl "https://api.dmchamp.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
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.dmchamp.com/v1/appointments/available-slots?${params}`,
{ headers: { "X-API-Key": "YOUR_API_KEY" } }
);
const data = await res.json();
console.log(data.data);
Python
import requests
res = requests.get(
"https://api.dmchamp.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):
{
"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= 查询表单)
curl -X POST "https://api.dmchamp.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
const res = await fetch("https://api.dmchamp.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
import requests
res = requests.post(
"https://api.dmchamp.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):
{
"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
curl "https://api.dmchamp.com/v1/appointments/aBcD1234eFgH5678" \
-H "X-API-Key: YOUR_API_KEY"
JavaScript
const res = await fetch(
"https://api.dmchamp.com/v1/appointments/aBcD1234eFgH5678",
{ headers: { "X-API-Key": "YOUR_API_KEY" } }
);
const data = await res.json();
console.log(data.appointment);
Python
import requests
res = requests.get(
"https://api.dmchamp.com/v1/appointments/aBcD1234eFgH5678",
headers={"X-API-Key": "YOUR_API_KEY"},
)
print(res.json()["appointment"])
响应 (200 OK):
{
"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
curl "https://api.dmchamp.com/v1/appointments?contact_id=contact_abc123&date=2026-06-15" \
-H "X-API-Key: YOUR_API_KEY"
JavaScript
const params = new URLSearchParams({
contact_id: "contact_abc123",
date: "2026-06-15",
});
const res = await fetch(
`https://api.dmchamp.com/v1/appointments?${params}`,
{ headers: { "X-API-Key": "YOUR_API_KEY" } }
);
const data = await res.json();
console.log(data.appointments, data.next_cursor);
Python
import requests
res = requests.get(
"https://api.dmchamp.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):
{
"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。请参阅 错误与分页 以了解共享的分页模式。
更新预约
PUT /appointments/{appointmentId}
重新安排预约或更改其详细信息。仅发送您想要更改的字段 — 至少需要一个字段。合并后的开始和结束时间必须保持时间顺序(end_time 必须在 start_time 之后)。更改会自动同步到关联的日历事件。
| 字段 | 描述 |
|---|---|
start_time |
新的开始时间,ISO 8601 日期时间格式。 |
end_time |
新的结束时间,ISO 8601 日期时间格式。必须晚于开始时间。 |
room_name |
新的房间或资源名称。 |
description |
新的描述,或使用 null 清除它。 |
summary |
新的摘要,或使用 null 清除它。 |
cURL
curl -X PUT "https://api.dmchamp.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
const res = await fetch(
"https://api.dmchamp.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
import requests
res = requests.put(
"https://api.dmchamp.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):
{
"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
curl -X POST "https://api.dmchamp.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
const res = await fetch(
"https://api.dmchamp.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
import requests
res = requests.post(
"https://api.dmchamp.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):
{
"success": true,
"appointment_id": "aBcD1234eFgH5678"
}
删除预约
DELETE /appointments/{appointmentId}
永久删除预约及其引用。如果您只想取消预订但保留记录,请改用 取消。
cURL
curl -X DELETE "https://api.dmchamp.com/v1/appointments/aBcD1234eFgH5678" \
-H "X-API-Key: YOUR_API_KEY"
JavaScript
const res = await fetch(
"https://api.dmchamp.com/v1/appointments/aBcD1234eFgH5678",
{ method: "DELETE", headers: { "X-API-Key": "YOUR_API_KEY" } }
);
const data = await res.json();
console.log(data.success);
Python
import requests
res = requests.delete(
"https://api.dmchamp.com/v1/appointments/aBcD1234eFgH5678",
headers={"X-API-Key": "YOUR_API_KEY"},
)
print(res.json()["success"])
响应 (200 OK):
{
"success": true
}
列出您已连接的 Google 日历
GET /appointments/google-calendars
直接从 Google 返回此账户上可用的 Google 日历——这对于向账户所有者显示一个选择器以决定从下方导入哪个日历,或者仅仅是为了确认连接是否处于活动状态非常有用。
只有在账户连接了 Google 日历(设置 → 集成)且至少拥有读取权限后,此功能才有效。如果尚未连接,或者授予的权限不再包含日历读取范围,您将收到一个 400,提示您(重新)连接它。
cURL
curl "https://api.dmchamp.com/v1/appointments/google-calendars" \
-H "X-API-Key: YOUR_API_KEY"
JavaScript
const res = await fetch("https://api.dmchamp.com/v1/appointments/google-calendars", {
headers: { "X-API-Key": "YOUR_API_KEY" },
});
const data = await res.json();
console.log(data.data);
Python
import requests
res = requests.get(
"https://api.dmchamp.com/v1/appointments/google-calendars",
headers={"X-API-Key": "YOUR_API_KEY"},
)
print(res.json()["data"])
响应 (200 OK):
{
"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 格式,因此字段名称遵循 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
curl -X POST "https://api.dmchamp.com/v1/appointments/import-calendar-events?apiKey=YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"agent_id": "agent_abc123",
"identifier": "EMAIL"
}'
JavaScript
const res = await fetch("https://api.dmchamp.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
import requests
res = requests.post(
"https://api.dmchamp.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):
{
"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}
curl "https://api.dmchamp.com/v1/appointments/import-calendar-events/jK9mQ2xR7pL4wN1t" \
-H "X-API-Key: YOUR_API_KEY"
响应 (200 OK):
{
"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 / OpenTable / TheFork / Trafft)
Zenchef 和 Formitable 是您的 AI 代理可以通过其预订真实餐桌的餐厅预订系统;Trafft 是一个面向预约业务的调度平台,每个账户连接一次,而不是每个餐厅连接一次。这两个餐厅平台各自都有一个公开的、无需身份验证的预订小部件(https://api.dmchamp.com/v1/zenchef-widget/... 和 https://api.dmchamp.com/v1/formitable-widget/...),可在聊天中为用餐者呈现——这些小部件路由是旨在在浏览器中打开的普通 HTML 页面,而不是 JSON API 端点,因此此处不予记录。以下是账户管理端点:验证餐厅 ID 是否属于账户持有人,然后添加、更新或删除它。
Zenchef
连接 Zenchef 餐厅需要进行两步验证,以确保账户持有人在将其接入机器人之前证明他们确实经营该餐厅:首先检查 ID 是否存在(不显示名称),然后让他们自行输入餐厅名称并验证其是否匹配。
第 1 步 — 检查餐厅 ID 是否存在
POST /appointments/zenchef-restaurants/check
| 字段 | 必填 | 描述 |
|---|---|---|
restaurant_id |
是 | 要检查的 Zenchef 餐厅 ID。 |
curl -X POST "https://api.dmchamp.com/v1/appointments/zenchef-restaurants/check?apiKey=YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "restaurant_id": "12345" }'
响应 (200 OK):
{
"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 上餐厅的真实名称进行比对(忽略大小写/空格)。 |
curl -X POST "https://api.dmchamp.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):
{
"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 步的已验证餐厅名称。 |
curl -X POST "https://api.dmchamp.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):
{ "success": true, "data": { "restaurantId": "12345" } }
更新已保存的 Zenchef 餐厅
PUT /appointments/zenchef-restaurants/{restaurantId}
| 字段 | 必填 | 说明 |
|---|---|---|
restaurant_name |
否 | 新的显示名称。 |
is_active |
否 | 设置 false 以阻止机器人预订该餐厅,而无需将其删除。 |
curl -X PUT "https://api.dmchamp.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}
curl -X DELETE "https://api.dmchamp.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"。 |
curl -X POST "https://api.dmchamp.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):
{
"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"。
curl "https://api.dmchamp.com/v1/appointments/formitable-restaurants/the-blue-door/details?language=en" \
-H "X-API-Key: YOUR_API_KEY"
响应 (200 OK):
{
"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)://。 |
curl -X POST "https://api.dmchamp.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。 |
curl -X PUT "https://api.dmchamp.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}
curl -X DELETE "https://api.dmchamp.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。
OpenTable
OpenTable 餐厅通过平台的 OpenTable 合作伙伴凭据进行访问,且 OpenTable 仅允许这些凭据查看在 OpenTable 集成市场内连接了该平台列表的餐厅。因此,与 Formitable 一样,一次验证调用就足够了:一个可访问的餐厅 ID(数字“RID”)既证明了餐厅的存在,也证明了它已连接该集成。在平台上的 OpenTable 合作伙伴列表启用之前,验证调用将返回 503。
验证 OpenTable 餐厅
POST /appointments/opentable-restaurants/verify
| 字段 | 必填 | 说明 |
|---|---|---|
restaurant_id |
是 | OpenTable 餐厅 ID (RID),例如 1038007 这样的数字。 |
curl -X POST "https://api.dmchamp.com/v1/appointments/opentable-restaurants/verify?apiKey=YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "restaurant_id": "1038007" }'
响应 (200 OK):
{
"success": true,
"data": {
"verified": true,
"restaurantDetails": {
"restaurantId": "1038007",
"diningAreaCount": 2,
"diningAreaNames": ["Main Room", "Garden"],
"tableTypes": ["default", "outdoor", "bar"]
}
}
}
verified: false 表示没有 OpenTable 餐厅拥有该 ID。403 表示餐厅存在,但尚未在 OpenTable 内连接平台的集成。速率限制为每个账户每 5 分钟 10 次尝试。
添加 OpenTable 餐厅
POST /appointments/opentable-restaurants
| 字段 | 必填 | 说明 |
|---|---|---|
restaurant_id |
是 | 已验证的餐厅 ID。 |
restaurant_name |
是 | 显示名称(标签;也是 AI 称呼该餐厅的方式)。 |
website_url |
否 | 当 AI 将客人转交给餐厅时向其显示的 http(s) URL。 |
curl -X POST "https://api.dmchamp.com/v1/appointments/opentable-restaurants?apiKey=YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "restaurant_id": "1038007", "restaurant_name": "The Blue Door", "website_url": "https://thebluedoor.example" }'
响应 (201 Created): { "success": true, "data": { "restaurantId": "1038007" } }
更新已保存的 OpenTable 餐厅
PUT /appointments/opentable-restaurants/{restaurantId}
发送 restaurant_name、is_active(使用 false 暂停)或 website_url(空字符串会清除它)中的任意项;省略的字段将保持不变。
curl -X PUT "https://api.dmchamp.com/v1/appointments/opentable-restaurants/1038007" \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "is_active": false }'
响应 (200 OK):{ "success": true, "data": { "restaurantId": "1038007" } }
移除 OpenTable 餐厅
DELETE /appointments/opentable-restaurants/{restaurantId}
curl -X DELETE "https://api.dmchamp.com/v1/appointments/opentable-restaurants/1038007" \
-H "X-API-Key: YOUR_API_KEY"
响应 (200 OK):{ "success": true, "data": { "restaurantId": "1038007" } }
更新当前账户中不存在的 restaurantId 时,将返回 404。
TheFork
TheFork 餐厅通过平台的 TheFork 合作伙伴凭据进行访问,且 TheFork 仅允许这些凭据查看在其 TheFork 账户中启用了该平台合作伙伴功能的餐厅。因此,与 Formitable 和 OpenTable 一样,一次验证调用就足够了:一个可访问的餐厅 ID 既证明了餐厅的存在,也证明了合作伙伴功能已在该餐厅启用。该 ID 是 TheFork 在 TheFork Manager 中分配给餐厅的 UUID,以字符串形式发送。在 TheFork 批准该平台作为合作伙伴并颁发凭据之前,验证调用将返回 503 ——有关其当前含义,请参阅 TheFork。
验证 TheFork 餐厅
POST /appointments/thefork-restaurants/verify
| 字段 | 必填 | 描述 |
|---|---|---|
restaurant_id |
是 | TheFork 餐厅 ID,一个 UUID,例如 9f2a1c34-5b6d-4e7f-8a90-1b2c3d4e5f60。 |
curl -X POST "https://api.dmchamp.com/v1/appointments/thefork-restaurants/verify?apiKey=YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "restaurant_id": "9f2a1c34-5b6d-4e7f-8a90-1b2c3d4e5f60" }'
响应 (200 OK):
{
"success": true,
"data": {
"verified": true,
"restaurantDetails": {
"restaurantId": "9f2a1c34-5b6d-4e7f-8a90-1b2c3d4e5f60",
"partySizes": [1, 2, 3, 4, 5, 6, 7, 8]
}
}
}
partySizes 是餐厅在未来 30 天内在线接受的就餐人数。返回 404 或 403 意味着 TheFork 未提供该 ID 的餐厅——要么是 ID 错误,要么是该餐厅尚未启用平台的合作伙伴功能;返回 400 意味着该 ID 不是 UUID。速率限制为每个账户每 5 分钟 10 次尝试。
添加 TheFork 餐厅
POST /appointments/thefork-restaurants
| 字段 | 必填 | 描述 |
|---|---|---|
restaurant_id |
是 | 已验证的餐厅 ID (UUID)。 |
restaurant_name |
是 | 显示名称(一个标签;也是 AI 称呼该餐厅的方式)。 |
website_url |
否 | 当 AI 将客人转交给餐厅时向其显示的 http(s) URL。 |
curl -X POST "https://api.dmchamp.com/v1/appointments/thefork-restaurants?apiKey=YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "restaurant_id": "9f2a1c34-5b6d-4e7f-8a90-1b2c3d4e5f60", "restaurant_name": "The Blue Door", "website_url": "https://thebluedoor.example" }'
响应 (201 Created): { "success": true, "data": { "restaurantId": "9f2a1c34-5b6d-4e7f-8a90-1b2c3d4e5f60" } }
更新已保存的 TheFork 餐厅
PUT /appointments/thefork-restaurants/{restaurantId}
发送 restaurant_name、is_active(使用 false 暂停)或 website_url(空字符串会清除它)中的任意项;省略的字段将保持不变。
curl -X PUT "https://api.dmchamp.com/v1/appointments/thefork-restaurants/9f2a1c34-5b6d-4e7f-8a90-1b2c3d4e5f60" \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "is_active": false }'
响应 (200 OK):{ "success": true, "data": { "restaurantId": "9f2a1c34-5b6d-4e7f-8a90-1b2c3d4e5f60" } }
移除 TheFork 餐厅
DELETE /appointments/thefork-restaurants/{restaurantId}
curl -X DELETE "https://api.dmchamp.com/v1/appointments/thefork-restaurants/9f2a1c34-5b6d-4e7f-8a90-1b2c3d4e5f60" \
-H "X-API-Key: YOUR_API_KEY"
响应 (200 OK):{ "success": true, "data": { "restaurantId": "9f2a1c34-5b6d-4e7f-8a90-1b2c3d4e5f60" } }
如果 restaurantId 当前不在账户中,更新或删除时将返回 404。
Trafft
Trafft 是针对整个账户连接一次,而不是针对每个地点:一个公司地址加上来自 Trafft 管理面板的 API 凭据(功能与集成 → API 与连接器,属于 Trafft 的商业计划)。连接调用会在存储任何内容之前根据 Trafft 检查这些凭据,因此错误的地址、错误的凭据或没有 API 访问权限的计划会在此处失败,而不是在客户对话中失败。客户端密钥以加密方式存储,且永远不会由任何端点返回。
所有三个写入调用(POST、PUT、DELETE)都需要集成 编辑 权限;状态 GET 需要集成 查看 权限。
连接 Trafft
POST /appointments/trafft/connect
| 字段 | 必填 | 描述 |
|---|---|---|
subdomain |
是 | 公司地址——您登录时 URL 中 .admin.trafft.com 之前的部分,例如 acme。接受完整地址并将其简化为相同的值。 |
client_id |
是 | 来自 Trafft 的 API 与连接器页面的客户端 ID。 |
client_secret |
是 | 来自同一页面的客户端密钥。以加密方式存储,从不返回。 |
company_name |
否 | 您自己列表的标签。 |
curl -X POST "https://api.dmchamp.com/v1/appointments/trafft/connect?apiKey=YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"subdomain": "acme",
"client_id": "YOUR_TRAFFT_CLIENT_ID",
"client_secret": "YOUR_TRAFFT_CLIENT_SECRET",
"company_name": "Acme Salon"
}'
响应 (200 OK):
{
"success": true,
"data": {
"subdomain": "acme",
"service_count": 12,
"employee_count": 4,
"location_count": 2
}
}
计数在检查期间从 Trafft 返回——这是确认凭据指向您预期的账户的最快方法。
获取连接状态
GET /appointments/trafft
curl "https://api.dmchamp.com/v1/appointments/trafft" \
-H "X-API-Key: YOUR_API_KEY"
响应 (200 OK):
{
"success": true,
"data": {
"connected": true,
"subdomain": "acme",
"hostname": "acme.admin.trafft.com",
"client_id": "YOUR_TRAFFT_CLIENT_ID",
"company_name": "Acme Salon",
"is_active": true,
"service_count": 12,
"employee_count": 4,
"location_count": 2
}
}
connected: false 表示尚未设置任何内容。此响应中从不包含客户端密钥。
更新连接
PUT /appointments/trafft
| 字段 | 必填 | 说明 |
|---|---|---|
is_active |
否 | 设置 false 以暂停 — AI 将停止向 Trafft 预订,但连接保持。 true 可恢复连接。 |
company_name |
否 | 新标签。 |
curl -X PUT "https://api.dmchamp.com/v1/appointments/trafft" \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "is_active": false }'
响应 (200 OK):与上述 GET 相同的连接对象。
断开 Trafft 连接
DELETE /appointments/trafft
curl -X DELETE "https://api.dmchamp.com/v1/appointments/trafft" \
-H "X-API-Key: YOUR_API_KEY"
响应 (200 OK):{ "success": true }
断开连接仅会移除存储的凭据。已在 Trafft 中的预约不受影响。
所有 Zenchef/Formitable/OpenTable/TheFork 端点的错误格式: 与本页面的其他部分不同,此处的错误会携带两次状态——一次作为 HTTP 状态,另一次作为正文中的
error_code——例如{ "success": false, "error": "Restaurant not found", "error_code": 404 }。处理方式与其他错误相同:检查success,并读取error以获取消息。
预约 API 错误
预约端点返回标准的错误封装:
{
"success": false,
"error": "Appointment not found"
}
| 状态码 | 在预约端点上发生的情况 |
|---|---|
400 |
缺少必填字段或字段无效 —— 例如错误的 start_time、end_time 不在 start_time 之后、无效的筛选器组合、没有要更新的字段,或预约已取消。 |
404 |
未找到预约、联系人或事件类型。 |
409 |
请求的时间段已被占用(预订冲突)。 |
每个端点都可能返回的共享代码 — 401, 403(您的套餐不包含 API 访问权限), 429(速率限制)和 500 — 及其重试指南列在 错误与分页 中。
使用您自己的 Google OAuth 客户端(日历许可界面)
当账户连接 Google 日历时,Google 的登录窗口会显示 OAuth 客户端的项目名称——默认情况下为平台的项目名称。代理商可以在其代理商账户上注册自己的 Google OAuth 2.0 客户端;此后,该账户及其下属的所有子账户的日历连接都将通过该客户端运行,因此许可界面会显示代理商的名称和徽标。其他一切保持不变:连接流程、双向同步以及上述预约端点的工作方式与之前完全相同。
仅限 Google 日历。 电子邮件渠道的 Gmail 邮箱 OAuth 不受影响。
您的客户首先需要准备什么
- 一个 OAuth 2.0 客户端,类型为 Google Cloud 项目中的 Web 应用程序,并在该项目中启用了 Google Calendar API。
- 每一个
redirect_uris条目(由下方的端点返回)都需添加到客户端的“已获授权的重定向 URI”中。第一个条目是您已验证的api.域名(如果您有的话)——Google 仅验证重定向地址位于您自有域名下的品牌——随后是平台的通用主机名,作为在此之前的备用地址。 - 许可界面,包含您的品牌、在“已获授权的域名”下的您的域名,以及声明的两个日历范围(响应中的
scopes)。在应用程序发布并经 Google 验证之前,用户会看到未经验证的应用程序警告,且客户端限制为 100 名用户。
保存您的客户端
PUT /account-config/google-oauth-client
| 字段 | 必填 | 描述 |
|---|---|---|
client_id |
是 | OAuth 2.0 客户端 ID,以 .apps.googleusercontent.com 结尾。 |
client_secret |
是 | 客户端密钥。在存储前会先与 Google 进行验证,然后加密。任何端点都不会返回此值。 |
curl -X PUT "https://api.dmchamp.com/v1/account-config/google-oauth-client?apiKey=YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"client_id": "123456789012-abcdefghijklmnop.apps.googleusercontent.com",
"client_secret": "GOCSPX-your-client-secret"
}'
响应
{
"success": true,
"configured": true,
"client_id": "123456789012-abcdefghijklmnop.apps.googleusercontent.com",
"redirect_uris": [
"https://api.youragency.com/v1/auth-google-callback",
"https://api.dmchamp.com/v1/auth-google-callback"
],
"scopes": [
"https://www.googleapis.com/auth/calendar.events",
"https://www.googleapis.com/auth/calendar.readonly"
],
"setup": ["…"]
}
错误的密钥或未知的客户端 ID 将被拒绝,并返回 400 以及 error 中 Google 给出的原因,且不会存储任何内容。
读取或移除它
GET /account-config/google-oauth-client 随时返回相同的摘要 — configured: false 以及在保存任何内容之前的 redirect_uris 和 scopes,因此您可以先设置 Google 端。 DELETE /account-config/google-oauth-client 会移除客户端:新的连接将恢复为平台客户端,并且通过已移除客户端连接的日历必须重新连接,因为只有发布连接的客户端才能刷新它。
团队成员需要 GET 的 Integrations: view 权限,以及 PUT / DELETE 的 Integrations: edit 权限。