BotPress中使用Cal.com API密钥与ID时预约请求失败
BotPress中Cal.com预约功能报错405的解决方案
核心问题分析
报错里的405状态码明确说明你使用的HTTP请求方法(POST)不被目标API端点支持。你的预约函数调用了错误的Cal.com API路径,/v1/event-types/{eventTypeID}/book不是创建预约的正确端点。
具体修正方案
1. 修复预约调度函数(scheduleCalComAppointment)
Cal.com创建预约的正确API端点是POST /v1/bookings,请求体需要包含eventTypeId、正确时区的start时间,以及用户信息。同时注意时间格式:直接拼接000Z会把时间转为UTC,但你的时区是America/New_York,需要正确生成对应时区的ISO时间。
修正后的代码:
async function action(input: scheduleCalComAppointmentInput): Promise<scheduleCalComAppointmentOutput> { try { const eventTypeID = 4640891 const apiKey = 'cal_live_a06a758a33fc98661777b1edbdf5053d' const timeZone = 'America/New_York' // 生成对应时区的ISO时间,避免时间偏差 const startTime = new Date(`${input.date}T${input.time}:00`).toLocaleString('en-US', { timeZone }) const isoStartTime = new Date(startTime).toISOString() const response = await axios.post( `https://api.cal.com/v1/bookings`, { eventTypeId: eventTypeID, start: isoStartTime, end: new Date(new Date(isoStartTime).getTime() + 60 * 60000).toISOString(), // 1小时时长 timeZone: timeZone, attendees: [ { name: input.name, email: input.email } ] }, { headers: { Authorization: `Bearer ${apiKey}`, 'Content-Type': 'application/json' }, timeout: 30000 } ) if (!response.data || !response.data.id) { throw new Error('Invalid response from Cal.com API') } return { eventId: response.data.id, status: 'success' } } catch (error) { throw new Error(`Failed to schedule appointment: ${error.response?.data?.message || error.message}`) } }
2. 修复可用性检查函数(checkCalcomAvailability)
你当前调用的是Cal.com的公开预约页面URL,不是官方API端点,这会导致响应格式不符合预期。正确的可用性检查API端点是GET /v1/event-types/{eventTypeId}/availability。
修正后的代码:
async function action(input: checkCalcomAvailabilityInput): Promise<checkCalcomAvailabilityOutput> { try { const date = input.date // e.g., 2026-02-20 const time = input.time // e.g., 14:30 const eventTypeId = 4640891 const apiKey = 'cal_live_a06a758a33fc98661777b1edbdf5053d' const timeZone = 'America/New_York' // 生成对应时区的时间范围 const startTime = new Date(`${date}T${time}:00`).toLocaleString('en-US', { timeZone }) const isoStartTime = new Date(startTime).toISOString() const isoEndTime = new Date(new Date(isoStartTime).getTime() + 60 * 60000).toISOString() const response = await axios.get(`https://api.cal.com/v1/event-types/${eventTypeId}/availability`, { params: { start: isoStartTime, end: isoEndTime, timeZone: timeZone }, headers: { Authorization: `Bearer ${apiKey}`, 'Content-Type': 'application/json' }, timeout: 30000 }) // 判断目标时间段是否在可用列表中 const isAvailable = response.data.some(slot => slot.start === isoStartTime && slot.end === isoEndTime ) return { isAvailable: isAvailable, message: isAvailable ? 'The requested time slot is available.' : 'The requested time slot is not available.' } } catch (error) { throw new Error(`Failed to check availability: ${error.response?.data?.message || error.message}`) } }
额外注意事项
- 确保你的Cal.com API密钥拥有
bookings:create和event-types:read的权限,否则会出现权限错误。 - 时间处理必须严格对应你的时区(America/New_York),避免时间偏差导致预约失败。
内容的提问来源于stack exchange,提问作者CBilpodiwala
相关产品推荐
相关产品推荐

