Skip to content

Commit 2526fc6

Browse files
plcclaude
andcommitted
Remove unused webhook_offsets field from calendars
webhook_offsets was a placeholder for pre-event reminder webhooks that was never implemented. Remove it from the schema, API handlers, docs, and specs to avoid shipping an unused field that causes confusion. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 016aced commit 2526fc6

File tree

8 files changed

+16
-31
lines changed

8 files changed

+16
-31
lines changed

CALDAVE_SPEC.md

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -352,12 +352,11 @@ Create a new calendar for the authenticated agent.
352352
"timezone": "America/Denver",
353353
"webhook_url": "https://example.com/webhook",
354354
"webhook_secret": "my_secret",
355-
"webhook_offsets": [300, 900],
356355
"welcome_event": false
357356
}
358357
```
359358

360-
Optional fields: `timezone` (default UTC), `webhook_url`, `webhook_secret`, `webhook_offsets`, `agentmail_api_key`, `welcome_event` (default true — set to false to skip the auto-created welcome event).
359+
Optional fields: `timezone` (default UTC), `webhook_url`, `webhook_secret`, `agentmail_api_key`, `welcome_event` (default true — set to false to skip the auto-created welcome event).
361360

362361
**Response:**
363362
```json
@@ -475,8 +474,7 @@ Set or update webhook configuration:
475474
```json
476475
{
477476
"webhook_url": "https://my-agent.fly.dev/hooks/calendar",
478-
"webhook_secret": "whsec_...",
479-
"webhook_offsets": ["-5m", "-1m", "0"]
477+
"webhook_secret": "whsec_..."
480478
}
481479
```
482480

@@ -697,7 +695,6 @@ For local development or offline use, run the STDIO server directly:
697695
| `feed_token` | `text` | Token for iCal feed auth |
698696
| `webhook_url` | `text` | Nullable |
699697
| `webhook_secret` | `text` | Nullable |
700-
| `webhook_offsets` | `jsonb` | Default `["-5m"]` |
701698
| `inbound_token` | `text` UNIQUE | `inb_` prefixed, for per-calendar webhook URL |
702699
| `agentmail_api_key` | `text` | Nullable, for fetching AgentMail attachments |
703700
| `created_at` | `timestamptz` | |

CHANGELOG.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
2121
- **Event tools**: `caldave_get_event`, `caldave_view_calendar`
2222
- **Debugging**: `caldave_list_errors`, `caldave_get_error`
2323
- **Discovery**: `caldave_get_changelog`, `caldave_get_manual`
24-
- **Missing parameters on existing MCP tools**`caldave_create_event` now supports `metadata`, `attendees`, and `status`. `caldave_update_event` now supports `metadata`, `attendees`, and `recurrence`. `caldave_create_calendar` now supports `webhook_url`, `webhook_secret`, `webhook_offsets`, `agentmail_api_key`, and `welcome_event`.
24+
- **Missing parameters on existing MCP tools**`caldave_create_event` now supports `metadata`, `attendees`, and `status`. `caldave_update_event` now supports `metadata`, `attendees`, and `recurrence`. `caldave_create_calendar` now supports `webhook_url`, `webhook_secret`, `agentmail_api_key`, and `welcome_event`.
2525
- **Remote MCP endpoint at `/mcp`** — CalDave now serves an MCP endpoint via Streamable HTTP transport. Agents can connect with just a URL and API key (`{ "url": "https://caldave.ai/mcp", "headers": { "Authorization": "Bearer sk_live_..." } }`) — no local installation required. Sessions are stateful with automatic 30-minute TTL cleanup.
2626
- **MCP agent guide** — Enhanced MCP instructions with structured quick-start, workflow descriptions, and tool selection guide. Added `caldave://guide` MCP resource with a comprehensive getting-started guide for agents (setup checklist, code examples, event fields reference, webhook/SMTP config, debugging).
2727

@@ -45,7 +45,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
4545
- **SMTP test `to` parameter**`POST /agents/smtp/test` now accepts an optional `to` body parameter to send the test email to a specific address instead of the configured `from` address.
4646
- **SMTP test endpoint**`POST /agents/smtp/test` sends a test email to verify your SMTP configuration works. Sends to the configured `from` address and reports success/failure with the SMTP error message if any.
4747
- **SMTP `secure` field**`PUT /agents/smtp` now accepts an optional `secure` boolean to explicitly control TLS mode. Use `true` for implicit TLS (port 465) or `false` for STARTTLS (port 587). Auto-detected from port when omitted.
48-
- **Webhook config at calendar creation**`POST /calendars` now accepts `webhook_url`, `webhook_secret`, and `webhook_offsets` at creation time, saving a separate `PATCH` call.
48+
- **Webhook config at calendar creation**`POST /calendars` now accepts `webhook_url` and `webhook_secret` at creation time, saving a separate `PATCH` call.
4949
- **`email_sent` in event responses**`POST` and `PATCH` event endpoints now return `email_sent: true/false` when the event has attendees, confirming whether the invite was dispatched. Invites are now sent synchronously before the response is returned.
5050
- **SMTP integration for outbound emails** — configure your own SMTP server via `PUT /agents/smtp` so calendar invites and RSVP replies are sent from your email address instead of CalDave's built-in delivery. New `GET /agents/smtp` (view config, password excluded) and `DELETE /agents/smtp` (revert to built-in). `GET /agents/me` now includes `smtp_configured` boolean. Supports any SMTP provider (AgentMail, SendGrid, Gmail, etc.).
5151
- **Webhook test endpoint**`POST /calendars/:id/webhook/test` sends a test payload to the calendar's configured webhook URL and returns the HTTP status code. Supports HMAC-SHA256 signing via `X-CalDave-Signature` when `webhook_secret` is set.

src/db.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@ async function initSchema() {
4141
feed_token text,
4242
webhook_url text,
4343
webhook_secret text,
44-
webhook_offsets jsonb DEFAULT '[\"-5m\"]',
4544
inbound_token text UNIQUE,
4645
created_at timestamptz NOT NULL DEFAULT now()
4746
);

src/lib/mcp-tools.mjs

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,7 @@ const INSTRUCTIONS = [
5353
'',
5454
'Webhooks',
5555
'--------',
56-
'Set webhook_url on a calendar to get notified before events start.',
57-
'Use webhook_offsets (e.g. ["-5m", "-15m"]) for advance reminders.',
56+
'Set webhook_url on a calendar to get notified when events change.',
5857
'Test with caldave_test_webhook before relying on it.',
5958
'',
6059
'Choosing the right tool',
@@ -178,8 +177,7 @@ export function registerTools(server, callApi, baseUrl, apiKey) {
178177
'## Webhook notifications',
179178
'',
180179
'Set `webhook_url` when creating/updating a calendar. CalDave sends a POST',
181-
'request before events start. Configure `webhook_offsets` to control timing',
182-
'(e.g. `["-5m", "-15m"]` for 5 and 15 minutes before).',
180+
'to your URL whenever events are created, updated, deleted, or responded to.',
183181
'',
184182
'If you set a `webhook_secret`, verify the `X-CalDave-Signature` header',
185183
'using HMAC-SHA256 on the raw request body.',
@@ -335,16 +333,14 @@ export function registerTools(server, callApi, baseUrl, apiKey) {
335333
timezone: z.string().optional().describe('IANA timezone (default: UTC)'),
336334
webhook_url: z.string().optional().describe('URL to receive event webhooks'),
337335
webhook_secret: z.string().optional().describe('Secret for HMAC-SHA256 webhook signatures'),
338-
webhook_offsets: z.array(z.union([z.number(), z.string()])).optional().describe('Offsets for pre-event webhook reminders (e.g. [300, 900] or ["-5m", "-1m"])'),
339336
agentmail_api_key: z.string().optional().describe('AgentMail API key for inbound email attachments'),
340337
welcome_event: z.boolean().optional().describe('Set to false to skip the auto-created welcome event (default: true)'),
341338
},
342-
async ({ name, timezone, webhook_url, webhook_secret, webhook_offsets, agentmail_api_key, welcome_event }) => {
339+
async ({ name, timezone, webhook_url, webhook_secret, agentmail_api_key, welcome_event }) => {
343340
const body = { name };
344341
if (timezone) body.timezone = timezone;
345342
if (webhook_url) body.webhook_url = webhook_url;
346343
if (webhook_secret) body.webhook_secret = webhook_secret;
347-
if (webhook_offsets) body.webhook_offsets = webhook_offsets;
348344
if (agentmail_api_key) body.agentmail_api_key = agentmail_api_key;
349345
if (welcome_event !== undefined) body.welcome_event = welcome_event;
350346
const data = await callApi('POST', '/calendars', body);
@@ -361,16 +357,14 @@ export function registerTools(server, callApi, baseUrl, apiKey) {
361357
timezone: z.string().optional().describe('IANA timezone'),
362358
webhook_url: z.string().optional().describe('URL to receive event notifications'),
363359
webhook_secret: z.string().optional().describe('HMAC secret for webhook signatures'),
364-
webhook_offsets: z.array(z.union([z.number(), z.string()])).optional().describe('Reminder offsets, e.g. ["-5m", "-1m"]'),
365360
agentmail_api_key: z.string().optional().describe('AgentMail API key'),
366361
},
367-
async ({ calendar_id, name, timezone, webhook_url, webhook_secret, webhook_offsets, agentmail_api_key }) => {
362+
async ({ calendar_id, name, timezone, webhook_url, webhook_secret, agentmail_api_key }) => {
368363
const body = {};
369364
if (name !== undefined) body.name = name;
370365
if (timezone !== undefined) body.timezone = timezone;
371366
if (webhook_url !== undefined) body.webhook_url = webhook_url;
372367
if (webhook_secret !== undefined) body.webhook_secret = webhook_secret;
373-
if (webhook_offsets !== undefined) body.webhook_offsets = webhook_offsets;
374368
if (agentmail_api_key !== undefined) body.agentmail_api_key = agentmail_api_key;
375369
const data = await callApi('PATCH', `/calendars/${calendar_id}`, body);
376370
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };

src/routes/calendars.js

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,9 @@ function formatCalendar(cal) {
5454
};
5555
}
5656

57-
const KNOWN_CALENDAR_POST_FIELDS = new Set(['name', 'timezone', 'agentmail_api_key', 'welcome_event', 'webhook_url', 'webhook_secret', 'webhook_offsets']);
57+
const KNOWN_CALENDAR_POST_FIELDS = new Set(['name', 'timezone', 'agentmail_api_key', 'welcome_event', 'webhook_url', 'webhook_secret']);
5858
const KNOWN_CALENDAR_PATCH_FIELDS = new Set([
59-
'name', 'timezone', 'webhook_url', 'webhook_secret', 'webhook_offsets', 'agentmail_api_key',
59+
'name', 'timezone', 'webhook_url', 'webhook_secret', 'agentmail_api_key',
6060
]);
6161

6262
function checkUnknownFields(body, knownFields) {
@@ -74,7 +74,7 @@ router.post('/', async (req, res) => {
7474
const unknownErr = checkUnknownFields(req.body, KNOWN_CALENDAR_POST_FIELDS);
7575
if (unknownErr) return res.status(400).json({ error: unknownErr });
7676

77-
const { name, timezone, agentmail_api_key, welcome_event, webhook_url, webhook_secret, webhook_offsets } = req.body;
77+
const { name, timezone, agentmail_api_key, welcome_event, webhook_url, webhook_secret } = req.body;
7878

7979
if (!name) {
8080
return res.status(400).json({ error: 'name is required' });
@@ -98,9 +98,9 @@ router.post('/', async (req, res) => {
9898
const tz = timezone || 'UTC';
9999

100100
await pool.query(
101-
`INSERT INTO calendars (id, agent_id, name, timezone, email, feed_token, inbound_token, agentmail_api_key, webhook_url, webhook_secret, webhook_offsets)
102-
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)`,
103-
[id, req.agent.id, name, tz, email, token, inbToken, agentmail_api_key || null, webhook_url || null, webhook_secret || null, webhook_offsets ? JSON.stringify(webhook_offsets) : null]
101+
`INSERT INTO calendars (id, agent_id, name, timezone, email, feed_token, inbound_token, agentmail_api_key, webhook_url, webhook_secret)
102+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)`,
103+
[id, req.agent.id, name, tz, email, token, inbToken, agentmail_api_key || null, webhook_url || null, webhook_secret || null]
104104
);
105105

106106
// Create welcome event unless explicitly opted out
@@ -188,7 +188,7 @@ router.patch('/:id', async (req, res) => {
188188
const unknownErr = checkUnknownFields(req.body, KNOWN_CALENDAR_PATCH_FIELDS);
189189
if (unknownErr) return res.status(400).json({ error: unknownErr });
190190

191-
const { name, timezone, webhook_url, webhook_secret, webhook_offsets, agentmail_api_key } = req.body;
191+
const { name, timezone, webhook_url, webhook_secret, agentmail_api_key } = req.body;
192192

193193
// Input validation
194194
if (name !== undefined && name.length > 255) {
@@ -212,7 +212,6 @@ router.patch('/:id', async (req, res) => {
212212
if (timezone !== undefined) { updates.push(`timezone = $${idx++}`); values.push(timezone); }
213213
if (webhook_url !== undefined) { updates.push(`webhook_url = $${idx++}`); values.push(webhook_url); }
214214
if (webhook_secret !== undefined) { updates.push(`webhook_secret = $${idx++}`); values.push(webhook_secret); }
215-
if (webhook_offsets !== undefined) { updates.push(`webhook_offsets = $${idx++}`); values.push(JSON.stringify(webhook_offsets)); }
216215
if (agentmail_api_key !== undefined) { updates.push(`agentmail_api_key = $${idx++}`); values.push(agentmail_api_key); }
217216

218217
if (updates.length === 0) {

src/routes/changelog.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,7 @@ const CHANGELOG = [
253253
{
254254
type: 'feature',
255255
title: 'Webhook config at calendar creation',
256-
description: 'POST /calendars now accepts webhook_url, webhook_secret, and webhook_offsets so you can configure webhooks in a single call instead of a separate PATCH.',
256+
description: 'POST /calendars now accepts webhook_url and webhook_secret so you can configure webhooks in a single call instead of a separate PATCH.',
257257
endpoints: ['POST /calendars'],
258258
docs: BASE + '/docs#post-calendars',
259259
},

src/routes/docs.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -399,7 +399,6 @@ const cachedHtml = `<!DOCTYPE html>
399399
<div class="param"><span class="param-name">agentmail_api_key <span class="param-opt">optional</span></span><span class="param-desc">AgentMail API key for fetching inbound email attachments</span></div>
400400
<div class="param"><span class="param-name">webhook_url <span class="param-opt">optional</span></span><span class="param-desc">URL to receive event webhooks (must be a valid URL)</span></div>
401401
<div class="param"><span class="param-name">webhook_secret <span class="param-opt">optional</span></span><span class="param-desc">Secret for HMAC-SHA256 webhook signatures (sent in <code class="inline-code">X-CalDave-Signature</code> header)</span></div>
402-
<div class="param"><span class="param-name">webhook_offsets <span class="param-opt">optional</span></span><span class="param-desc">Reserved for future pre-event reminders. Stored but not yet acted on.</span></div>
403402
<div class="param"><span class="param-name">welcome_event <span class="param-opt">optional</span></span><span class="param-desc">Set to <code class="inline-code">false</code> to skip the auto-created welcome event (recommended for production agents). Defaults to true.</span></div>
404403
</div>
405404
<div class="label">Example</div>
@@ -457,7 +456,6 @@ const cachedHtml = `<!DOCTYPE html>
457456
<div class="param"><span class="param-name">timezone</span><span class="param-desc">IANA timezone</span></div>
458457
<div class="param"><span class="param-name">webhook_url</span><span class="param-desc">URL to receive event notifications</span></div>
459458
<div class="param"><span class="param-name">webhook_secret</span><span class="param-desc">HMAC secret for webhook signatures</span></div>
460-
<div class="param"><span class="param-name">webhook_offsets</span><span class="param-desc">Reserved for future pre-event reminders</span></div>
461459
<div class="param"><span class="param-name">agentmail_api_key</span><span class="param-desc">AgentMail API key for this calendar</span></div>
462460
</div>
463461
<div class="label">Example</div>

src/routes/man.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,6 @@ function getEndpoints() {
241241
{ name: 'agentmail_api_key', in: 'body', required: false, type: 'string', description: 'AgentMail API key for inbound email attachments' },
242242
{ name: 'webhook_url', in: 'body', required: false, type: 'string', description: 'URL to receive event webhooks' },
243243
{ name: 'webhook_secret', in: 'body', required: false, type: 'string', description: 'Secret for HMAC-SHA256 webhook signatures' },
244-
{ name: 'webhook_offsets', in: 'body', required: false, type: 'array', description: 'Reserved for future pre-event reminders. Stored but not yet acted on.' },
245244
{ name: 'welcome_event', in: 'body', required: false, type: 'boolean', description: 'Set to false to skip the auto-created welcome event (recommended for production). Defaults to true.' },
246245
],
247246
example_body: { name: 'Work Schedule', timezone: 'America/Denver' },
@@ -288,7 +287,6 @@ function getEndpoints() {
288287
{ name: 'timezone', in: 'body', required: false, type: 'string', description: 'IANA timezone' },
289288
{ name: 'webhook_url', in: 'body', required: false, type: 'string', description: 'URL to receive event notifications' },
290289
{ name: 'webhook_secret', in: 'body', required: false, type: 'string', description: 'HMAC secret for webhook signatures' },
291-
{ name: 'webhook_offsets', in: 'body', required: false, type: 'array', description: 'Reserved for future pre-event reminders. Stored but not yet acted on.' },
292290
{ name: 'agentmail_api_key', in: 'body', required: false, type: 'string', description: 'AgentMail API key' },
293291
],
294292
example_body: { name: 'Updated Name', timezone: 'America/New_York' },

0 commit comments

Comments
 (0)