Calendar API
Authenticate every request with your API key:
CODEBLOCK0
API keys are created from the Agents tab in the dashboard, or via the setup flow below. Each key
is scoped to specific calendars with granular permissions: can_read, can_create, can_update,
can_delete, can_create_invitees, and can_update_invitees.
Setup (getting an API key)
If you don't have a cal_ API key yet, use the setup flow to let the user authorize access:
- 1. Create a setup session (no auth required):
POST https://cal.limehouse.io/api/v1/setup/sessions
Response: INLINECODE7
- 2. Direct the user to open
setup_url in their browser. They will sign in, choose calendars, and
configure permissions.
- 3. Poll for completion (every 3 seconds):
GET https://cal.limehouse.io/api/v1/setup/sessions/{token}/poll
Response while pending:
{ "status": "pending" }
Response when complete: INLINECODE10
- 4. Store the
agent_token value. This is your API key for all subsequent requests.
The setup session expires after 30 minutes. If the agent cannot make HTTP requests, direct the user
to https://cal.limehouse.io/connect where they can create a key and copy it manually.
Get agent info
CODEBLOCK3
Get metadata about the current agent, including its name, description, permitted calendars with per-calendar permissions, and calendars the user owns that the agent does not yet have access to (connectedcalendarswithout_access).
Response
CODEBLOCK4
Use connected_calendars_without_access to discover calendars the agent could
request access to via POST /api/v1/agent/request-permission-change.
List accessible calendars
CODEBLOCK5
List all calendars the agent has access to, along with read/write permissions.
Response — array of calendar objects:
[
{ "id": 3, "name": "Work", "color": "#4285F4", "timezone": "America/New_York" }
]
List events
CODEBLOCK7
List events on a calendar, optionally filtered by a time range. If no calendar_id is provided, returns events from all calendars the agent has read access to. start and end accept either a full ISO 8601 datetime (e.g. "2026-03-05T00:00:00-08:00") or a date-only string (e.g. "2026-03-05") which is expanded to the full day in the user's timezone. Requires can_read. Filter results with optional query parameters.
| Parameter | Type | Required | Description |
|---|
| INLINECODE16 | integer | No | Filter to a specific calendar. If omitted, returns events from all readable calendars. |
| INLINECODE17 |
ISO 8601 datetime or date | No | ISO 8601 datetime for the event start. When a timezone parameter is provided, the datetime is interpreted as wall-clock time in that timezone (any UTC offset is ignored). Otherwise, include a UTC offset for non-UTC times, e.g. "2026-02-26T17:30:00-08:00". Naive datetimes (without offset) are treated as UTC. Also accepts a date-only string (e.g. "2026-03-05") which expands to start of day in the user's timezone. |
|
end | ISO 8601 datetime or date | No | ISO 8601 datetime for the event end. Same timezone rules as start_time. Also accepts a date-only string which expands to end of day in the user's timezone. |
Response — array of event objects:
[
{
"id": 42,
"uid": "abc123",
"calendar_id": 3,
"summary": "Team standup",
"description": "Daily sync",
"location": "Conference Room A",
"start_time": "2024-01-15T09:00:00Z",
"end_time": "2024-01-15T09:30:00Z",
"all_day": false,
"recurrence_rule": null,
"invitees": [
{"email": "alice@example.com", "name": "Alice", "rsvp": "ACCEPTED"},
{"email": "bob@example.com", "name": null, "rsvp": "NEEDS-ACTION"}
],
"etag": "v1",
"created_at": "2024-01-01T00:00:00Z",
"updated_at": "2024-01-01T00:00:00Z"
}
]
Create an event
CODEBLOCK9
Create a new event on a calendar. Requires can_create. Returns 201 Created with the new event object. If invitees is non-empty, also requires can_create_invitees.
| Field | Type | Required | Description |
|---|
| INLINECODE23 | string | Yes | Event title (1–255 chars) |
| INLINECODE24 |
string | No | Event description |
|
location | string | No | Physical address or virtual meeting link for the event. |
|
start_time | ISO 8601 datetime | Yes | ISO 8601 datetime for the event start. When a timezone parameter is provided, the datetime is interpreted as wall-clock time in that timezone (any UTC offset is ignored). Otherwise, include a UTC offset for non-UTC times, e.g. "2026-02-26T17:30:00-08:00". Naive datetimes (without offset) are treated as UTC. |
|
end_time | ISO 8601 datetime | Yes | ISO 8601 datetime for the event end. Same timezone rules as start_time. |
|
all_day | boolean | No | Default:
false |
|
timezone | string | No | IANA timezone name (e.g. "America/Los
Angeles"). When provided, starttime and end_time are interpreted as wall-clock times in this timezone, and DST is handled automatically. |
|
recurrence_rule| string | No | RRULE string for recurring events (e.g. "FREQ=WEEKLY;BYDAY=MO").|
|
invitees | array | No | List of attendees. Each item:
{"email": "...", "name": "..."}. For Google-connected calendars, Google will send invite emails (
sendUpdates=all). |
Example request body
{
"summary": "Team standup",
"description": "Daily sync",
"location": "Conference Room A",
"start_time": "2024-01-15T09:00:00",
"end_time": "2024-01-15T09:30:00",
"timezone": "America/Los_Angeles",
"invitees": [
{"email": "alice@example.com", "name": "Alice"},
{"email": "bob@example.com"}
]
}
Update an event
CODEBLOCK11
Update an existing event on a calendar. Only include the fields you want to change — omitted fields are left untouched. Never include starttime or endtime unless you are explicitly changing the time. Requires can_update. Returns 200 OK with the updated event object. If invitees is included in the request body, also requires can_update_invitees.
Sending invitees replaces the full attendee list (pass [] to remove all invitees).
| Field | Type | Description |
|---|
| INLINECODE41 | string | Event title (1–255 chars) |
| INLINECODE42 |
string | Event description |
|
location | string | Physical address or virtual meeting link for the event. |
|
start_time | ISO 8601 datetime | ISO 8601 datetime for the event start. When a timezone parameter is provided, the datetime is interpreted as wall-clock time in that timezone (any UTC offset is ignored). Otherwise, include a UTC offset for non-UTC times, e.g. "2026-02-26T17:30:00-08:00". Naive datetimes (without offset) are treated as UTC. |
|
end_time | ISO 8601 datetime | ISO 8601 datetime for the event end. Same timezone rules as start_time. |
|
all_day | boolean | Whether the event spans the full day |
|
timezone | string | IANA timezone name (e.g. "America/Los
Angeles"). When provided, starttime and end_time are interpreted as wall-clock times in this timezone, and DST is handled automatically. |
|
recurrence_rule| string | RRULE string for recurring events (e.g. "FREQ=WEEKLY;BYDAY=MO").|
|
invitees | array | Replace full attendee list (pass
[] to clear) |
Example — rename an event (only send the field that changes):
CODEBLOCK12
Example — reschedule (only when intentionally changing the time):
{
"start_time": "2024-01-15T10:00:00",
"end_time": "2024-01-15T10:30:00",
"timezone": "America/Los_Angeles"
}
Delete an event
CODEBLOCK14
Delete an event from a calendar. Requires can_delete. Returns 204 No Content.
Add travel time
CODEBLOCK15
Add a travel time buffer event before an existing event using Google Maps directions. Creates a new event that ends when the target event starts, with duration based on the calculated travel time. Requires can_read + can_create.
The target event must have a location set.
| Field | Type | Required | Description |
|---|
| INLINECODE56 | string | Yes | Travel mode — one of "walking", "driving", "biking", or "transit". For transit, the route is planned to arrive by the event's start time. |
| INLINECODE57 |
string | No | Where the user is traveling from — "home", "work", or "custom". If not specified, uses the location of the previous event on the same day, or falls back to the user's home address. |
|
start_location_address | string | No | Custom start address. Required when start
locationtype is "custom". |
Example request body
CODEBLOCK16
Response — 201 Created:
CODEBLOCK17
The user's home and work addresses are set via PUT /api/v1/auth/me/preferences with
home_address and/or work_address fields.
Request a calendar connection
CODEBLOCK18
Request the user to connect a new calendar provider. Returns a confirmation URL that the user must visit to authorize the connection. The agent cannot connect the calendar directly — the user must click the link and approve.
| Field | Type | Required | Description |
|---|
| INLINECODE63 | string | Yes | The calendar provider to connect — "google" or "microsoft". |
Response — 201 Created:
CODEBLOCK19
Surface the confirmation_url in chat so the user can click it.
Request a permission change
CODEBLOCK20
Request the user to grant or change this agent's permissions on a calendar. Returns a confirmation URL that the user must visit to approve the change. The requested permissions are suggestions — the user can adjust them before confirming.
| Field | Type | Required | Description |
|---|
| INLINECODE66 | integer | Yes | The ID of the calendar. |
| INLINECODE67 |
object | Yes | Permission flags (see below) |
requested_permissions fields (all boolean, all optional — defaults to current value):
| Key | Description |
|---|
| INLINECODE69 | Request read access to events on this calendar. |
| INLINECODE70 |
Request permission to create events on this calendar. |
|
can_update | Request permission to update events on this calendar. |
|
can_delete | Request permission to delete events on this calendar. |
|
can_create_invitees| Add invitees when creating events |
|
can_update_invitees| Modify invitees when updating |
Response — 201 Created:
CODEBLOCK21
If a matching pending request already exists, returns 409 with the existing URL.
Tip: Call GET /api/v1/agent/me first to see which calendars the agent already
has access to and which ones it doesn't (connected_calendars_without_access).
Error responses
| Status | Meaning |
|---|
| INLINECODE79 | Missing or invalid API key |
| INLINECODE80 |
Key exists but lacks permission — use
request-permission-change |
|
404 Not Found | Calendar or event does not exist |
|
409 Conflict | A pending confirmation already exists (returns the existing URL) |
Quick-start (curl)
CODEBLOCK22
日历 API
使用您的 API 密钥验证每个请求:
Authorization: Bearer cal_
API 密钥通过仪表盘的代理选项卡创建,或通过以下设置流程生成。每个密钥都限定于特定日历,并具有细粒度权限:canread(可读取)、cancreate(可创建)、canupdate(可更新)、candelete(可删除)、cancreateinvitees(可创建受邀者)和 canupdateinvitees(可更新受邀者)。
设置(获取 API 密钥)
如果您还没有 cal_ API 密钥,请使用设置流程让用户授权访问:
- 1. 创建设置会话(无需认证):
POST https://cal.limehouse.io/api/v1/setup/sessions
响应:{ token: ..., setupurl: https://..., expiresat: ... }
- 2. 引导用户在浏览器中打开 setup_url。他们将登录、选择日历并配置权限。
- 3. 轮询完成状态(每 3 秒一次):
GET https://cal.limehouse.io/api/v1/setup/sessions/{token}/poll
等待中响应:{ status: pending }
完成时响应:{ status: completed, agenttoken: cal... }
- 4. 存储 agent_token 值。这就是您所有后续请求的 API 密钥。
设置会话在 30 分钟后过期。如果代理无法发出 HTTP 请求,请引导用户访问 https://cal.limehouse.io/connect,他们可以在那里手动创建并复制密钥。
获取代理信息
GET /api/v1/agent/me
获取当前代理的元数据,包括其名称、描述、具有逐日历权限的已授权日历,以及用户拥有但代理尚未获得访问权限的日历(connectedcalendarswithout_access)。
响应
json
{
id: 1,
name: My Agent,
description: optional description,
created_at: 2024-01-01T00:00:00,
last_used: 2024-01-02T12:00:00,
permitted_calendars: [
{
calendar_id: 3,
calendar_name: Work,
can_read: true,
can_create: false,
can_update: false,
can_delete: false,
cancreateinvitees: false,
canupdateinvitees: false
}
],
connectedcalendarswithout_access: [
{
calendar_id: 5,
calendar_name: Side Projects
}
]
}
使用 connectedcalendarswithout_access 发现代理可以通过 POST /api/v1/agent/request-permission-change 请求访问权限的日历。
列出可访问的日历
GET /api/v1/agent/calendars
列出代理有权访问的所有日历及其读写权限。
响应 — 日历对象数组:
json
[
{ id: 3, name: Work, color: #4285F4, timezone: America/New_York }
]
列出事件
GET /api/v1/agent/events
GET /api/v1/agent/calendars/{calendar_id}/events
列出日历上的事件,可选择按时间范围过滤。如果未提供 calendarid,则返回代理具有读取权限的所有日历中的事件。start 和 end 接受完整的 ISO 8601 日期时间(例如 2026-03-05T00:00:00-08:00)或仅日期字符串(例如 2026-03-05),后者会扩展为用户所在时区的全天。需要 canread 权限。使用可选的查询参数过滤结果。
| 参数 | 类型 | 必需 | 描述 |
|---|
| calendar_id | 整数 | 否 | 过滤到特定日历。如果省略,则返回所有可读日历中的事件。 |
| start |
ISO 8601 日期时间或日期 | 否 | 事件开始的 ISO 8601 日期时间。当提供了时区参数时,日期时间被解释为该时区的挂钟时间(忽略任何 UTC 偏移)。否则,对于非 UTC 时间,请包含 UTC 偏移,例如 2026-02-26T17:30:00-08:00。无偏移的原始日期时间被视为 UTC。也接受仅日期字符串(例如 2026-03-05),该字符串扩展为用户所在时区的当天开始时间。 |
| end | ISO 8601 日期时间或日期 | 否 | 事件结束的 ISO 8601 日期时间。时区规则与 start_time 相同。也接受仅日期字符串,该字符串扩展为用户所在时区的当天结束时间。 |
响应 — 事件对象数组:
json
[
{
id: 42,
uid: abc123,
calendar_id: 3,
summary: Team standup,
description: Daily sync,
location: Conference Room A,
start_time: 2024-01-15T09:00:00Z,
end_time: 2024-01-15T09:30:00Z,
all_day: false,
recurrence_rule: null,
invitees: [
{email: alice@example.com, name: Alice, rsvp: ACCEPTED},
{email: bob@example.com, name: null, rsvp: NEEDS-ACTION}
],
etag: v1,
created_at: 2024-01-01T00:00:00Z,
updated_at: 2024-01-01T00:00:00Z
}
]
创建事件
POST /api/v1/agent/calendars/{calendar_id}/events
在日历上创建新事件。需要 cancreate 权限。返回 201 Created 及新事件对象。如果 invitees 非空,则还需要 cancreate_invitees 权限。
| 字段 | 类型 | 必需 | 描述 |
|---|
| summary | 字符串 | 是 | 事件标题(1–255 个字符) |
| description |
字符串 | 否 | 事件描述 |
| location | 字符串 | 否 | 事件的物理地址或虚拟会议链接。 |
| start_time | ISO 8601 日期时间 | 是 | 事件开始的 ISO 8601 日期时间。当提供了时区参数时,日期时间被解释为该时区的挂钟时间(忽略任何 UTC 偏移)。否则,对于非 UTC 时间,请包含 UTC 偏移,例如 2026-02-26T17:30:00-08:00。无偏移的原始日期时间被视为 UTC。 |
| end
time | ISO 8601 日期时间 | 是 | 事件结束的 ISO 8601 日期时间。时区规则与 starttime 相同。 |
| all_day | 布尔值 | 否 | 默认值:false |
| timezone | 字符串 | 否 | IANA 时区名称(例如 America/Los
Angeles)。提供时,starttime 和 end_time 被解释为该时区的挂钟时间,并自动处理夏令时。 |
| recurrence_rule| 字符串 | 否 | 重复事件的 RRULE 字符串(例如 FREQ=WEEKLY;BYDAY=MO)。 |
| invitees | 数组 | 否 | 参与者列表。每个项目:{email: ..., name: ...}。对于 Google 连接的日历,Google 将发送邀请邮件(sendUpdates=all)。 |
示例请求体
json
{
summary: Team standup,
description: Daily sync,
location: Conference Room A,
start_time: 2024-01-15T09:00:00,
end_time: 2024-01-15T09:30:00,
timezone: America/Los_Angeles,
invitees: [
{email: alice@example.com, name: Alice},
{email: bob@example.com}
]
}
更新事件
PUT /api/v1/agent/calendars/{calendarid}/events/{eventid}