CalDave API ← Home Sign Up Log In

Calendar-as-a-service for AI agents. Base URL: https://caldave.ai

AI agents: This is a REST API. Use curl or HTTP requests from your code — not a browser. Start with GET https://caldave.ai/man for a machine-readable guide.
AI agents: CalDave is actively evolving. Poll GET /changelog weekly to discover new endpoints and capabilities. Pass your Bearer token to /changelog and GET /man for personalized results — see what changed since you signed up and get recommendations for your next steps.

Endpoints

Human Accounts
Agents
Calendars
Events
Feeds & Inbound Email
Event Webhooks
Debugging
Discovery
Reference

Authentication

Most endpoints require a Bearer token. Include it in every request:

Authorization: Bearer YOUR_API_KEY

Exceptions: POST /agents (no auth), GET /feeds (token in query param), and POST /inbound (token in URL path).

In curl examples below, YOUR_API_KEY, CAL_ID, EVT_ID, and FEED_TOKEN are placeholders — replace them with your real values.

Error Handling

All error responses return a JSON object with a single error field:

{ "error": "Human-readable message" }
Status codes
200Success
201Created
204Deleted (no body)
400Validation error — check the error message for details
401Missing or invalid API key
404Resource not found
429Rate limited — back off and retry after the interval in RateLimit-Reset
500Server error — retry or check GET /errors for details
Every response includes rate limit headers: RateLimit-Limit, RateLimit-Remaining, and RateLimit-Reset (RFC draft-7). Monitor these to avoid 429 responses.

Rate Limits

API endpoints1000 requests / minute per IP
POST /agents20 requests / hour per IP
Login / Signup10 requests / 15 min per IP
Inbound webhooks60 requests / minute per IP

Responses include RateLimit-Limit, RateLimit-Remaining, and RateLimit-Reset headers (RFC draft-7). When exceeded, you receive a 429 response.

Human Accounts

Human accounts let you manage your agent keys from a central dashboard. Sign up at /signup, then claim agents by providing their secret key. You can also pass the X-Human-Key header on POST /agents or POST /agents/claim for programmatic access.
POST /agents/claim X-Human-Key

Claim an existing agent by providing its API key. The agent is immediately associated with your human account.

Headers
X-Human-Key requiredYour human API key (hk_live_...)
Body parameters
api_key requiredThe agent API key to claim (sk_live_...)
Example
curl -s -X POST "https://caldave.ai/agents/claim" \
  -H "Content-Type: application/json" \
  -H "X-Human-Key: hk_live_YOUR_HUMAN_KEY" \
  -d '{"api_key": "sk_live_AGENT_KEY_TO_CLAIM"}'

Agents

POST /agents No auth required

Create a new agent identity. Returns credentials you must save — the API key is shown once. Include name and description to identify your agent — the name appears in outbound email From headers.

Headers
X-Human-Key optionalYour human API key (hk_live_...). When provided, the new agent is automatically associated with your human account.
Body parameters
name recommendedDisplay name for the agent (max 255 chars). Appears in outbound email From headers (e.g. "My Agent" <cal-xxx@invite.caldave.ai>).
description recommendedWhat the agent does (max 1024 chars). Surfaced in GET /man personalized context.
Example
curl -s -X POST "https://caldave.ai/agents" \
  -H "Content-Type: application/json" \
  -d '{"name": "Meeting Scheduler", "description": "Books rooms and sends reminders"}'
With human key (auto-associate)
curl -s -X POST "https://caldave.ai/agents" \
  -H "Content-Type: application/json" \
  -H "X-Human-Key: hk_live_YOUR_HUMAN_KEY" \
  -d '{"name": "Meeting Scheduler"}'
Response
{
  "agent_id": "agt_x7y8z9AbCd",
  "api_key": "sk_live_abc123...",
  "name": "Meeting Scheduler",
  "description": "Books rooms and sends reminders",
  "owned_by": "hum_abc123...",
  "message": "Store these credentials securely. The API key will not be shown again."
}
The owned_by field only appears when X-Human-Key is provided. Without it, the agent is created unclaimed.
GET /agents/me Bearer token

Get the authenticated agent's profile.

Example
curl -s "https://caldave.ai/agents/me" \
  -H "Authorization: Bearer YOUR_API_KEY"
Response
{
  "agent_id": "agt_x7y8z9AbCd",
  "name": "Meeting Scheduler",
  "description": "Books rooms and sends reminders",
  "created_at": "2026-02-14T10:30:00.000Z"
}
PATCH /agents Bearer token

Update agent metadata. Does not change the API key. Set a field to null to clear it.

Body parameters
nameDisplay name (max 255 chars)
descriptionWhat the agent does (max 1024 chars)
Example
curl -s -X PATCH "https://caldave.ai/agents" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -d '{"name": "Updated Name"}'
Response
{
  "agent_id": "agt_x7y8z9AbCd",
  "name": "Updated Name",
  "description": "Books rooms and sends reminders",
  "created_at": "2026-02-14T10:30:00.000Z"
}
When an agent has a name, outbound invite and RSVP reply emails use it as the From display name (e.g. "Meeting Scheduler" <cal-xxx@invite.caldave.ai>).
PUT /agents/smtp Bearer token

Configure an SMTP server for outbound emails. When set, all invite and RSVP emails for this agent are sent via your SMTP server instead of CalDave's built-in delivery. This lets invites come from your own email address.

Body parameters
host requiredSMTP server hostname (e.g. smtp.agentmail.to)
port requiredSMTP port (465 for SSL, 587 for STARTTLS)
username requiredSMTP auth username
password requiredSMTP auth password (stored securely, never returned in responses)
from requiredFrom email address for outbound emails
secure optionalUse implicit TLS (true) or STARTTLS (false). Defaults to true for port 465, false otherwise.
Example
curl -s -X PUT "https://caldave.ai/agents/smtp" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -d '{"host": "smtp.agentmail.to", "port": 465, "username": "inbox@agentmail.to", "password": "YOUR_SMTP_PASSWORD", "from": "inbox@agentmail.to"}'
Response
{
  "smtp": {
    "host": "smtp.agentmail.to",
    "port": 465,
    "username": "inbox@agentmail.to",
    "from": "inbox@agentmail.to",
    "secure": true,
    "configured": true
  }
}
Password is never included in responses. When SMTP is configured, the from address replaces the calendar email as the From address on all outbound emails. If your agent has a name, it appears as the display name (e.g. "My Agent" <inbox@agentmail.to>).
GET /agents/smtp Bearer token

View the current SMTP configuration (password excluded).

Example
curl -s "https://caldave.ai/agents/smtp" \
  -H "Authorization: Bearer YOUR_API_KEY"
DELETE /agents/smtp Bearer token

Remove the SMTP configuration. Outbound emails revert to CalDave's built-in delivery.

Example
curl -s -X DELETE "https://caldave.ai/agents/smtp" \
  -H "Authorization: Bearer YOUR_API_KEY"
POST /agents/smtp/test Bearer token

Send a test email to verify your SMTP configuration works. Defaults to the configured from address; override with to.

Body parameters
to optionalRecipient email address. Defaults to the configured from address.
Example
curl -s -X POST "https://caldave.ai/agents/smtp/test" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -d '{"to": "test@example.com"}'
Response
{
  "success": true,
  "message_id": "<abc123@smtp.agentmail.to>",
  "from": "inbox@agentmail.to",
  "to": "test@example.com",
  "message": "Test email sent successfully to test@example.com."
}

Calendars

POST /calendars Bearer token

Create a new calendar for the authenticated agent.

Body parameters
name requiredCalendar display name
timezone optionalIANA timezone (default: UTC)
agentmail_api_key optionalAgentMail API key for fetching inbound email attachments
webhook_url optionalURL to receive event webhooks (must be a valid URL)
webhook_secret optionalSecret for HMAC-SHA256 webhook signatures (sent in X-CalDave-Signature header)
welcome_event optionalSet to false to skip the auto-created welcome event (recommended for production agents). Defaults to true.
Example
curl -s -X POST "https://caldave.ai/calendars" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -d '{"name": "Work Schedule", "timezone": "America/Denver"}'
Response
{
  "calendar_id": "cal_a1b2c3XyZ",
  "name": "Work Schedule",
  "timezone": "America/Denver",
  "email": "cal-a1b2c3XyZ@invite.caldave.ai",
  "ical_feed_url": "https://caldave.ai/feeds/cal_a1b2c3XyZ.ics?token=feed_...",
  "feed_token": "feed_...",
  "inbound_webhook_url": "https://caldave.ai/inbound/inb_...",
  "message": "This calendar can receive invites at cal-a1b2c3XyZ@invite.caldave.ai. ..."
}
GET /calendars Bearer token

List all calendars for the authenticated agent.

Example
curl -s "https://caldave.ai/calendars" \
  -H "Authorization: Bearer YOUR_API_KEY"
GET /calendars/:id Bearer token

Get a single calendar by ID.

Example
curl -s "https://caldave.ai/calendars/CAL_ID" \
  -H "Authorization: Bearer YOUR_API_KEY"
PATCH /calendars/:id Bearer token

Update calendar settings. All fields are optional.

Body parameters
nameCalendar display name
timezoneIANA timezone
webhook_urlURL to receive event notifications
webhook_secretHMAC secret for webhook signatures
agentmail_api_keyAgentMail API key for this calendar
Example
curl -s -X PATCH "https://caldave.ai/calendars/CAL_ID" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -d '{"name": "Updated Name", "timezone": "America/New_York"}'
DELETE /calendars/:id Bearer token

Delete a calendar and all its events. Returns 204 on success.

Example
curl -s -X DELETE "https://caldave.ai/calendars/CAL_ID" \
  -H "Authorization: Bearer YOUR_API_KEY"
POST /calendars/:id/webhook/test Bearer token

Send a test payload to the calendar's configured webhook URL. Returns the HTTP status code from the webhook endpoint. Useful for verifying webhook configuration before real events fire.

Example
curl -s -X POST "https://caldave.ai/calendars/CAL_ID/webhook/test" \
  -H "Authorization: Bearer YOUR_API_KEY"
Response
{
  "success": true,
  "status_code": 200,
  "webhook_url": "https://example.com/webhook",
  "message": "Webhook delivered successfully."
}
The test payload includes type: "test" so your webhook handler can distinguish test pings from real events. If webhook_secret is set, the payload is signed with HMAC-SHA256 via the X-CalDave-Signature header.

Event Webhooks

Event webhooks notify your server when calendar events change. This is separate from inbound email (which creates events from forwarded .ics invites).

To enable: Set webhook_url on a calendar via POST /calendars or PATCH /calendars/:id. Optionally set webhook_secret for HMAC-SHA256 signature verification. Use POST /calendars/:id/webhook/test to verify your endpoint is reachable.

Webhook Verification

When webhook_secret is set on a calendar, CalDave signs every webhook payload with HMAC-SHA256. The hex digest is sent in the X-CalDave-Signature header. Verify it on your server to confirm the request came from CalDave and wasn't tampered with.

How it works
1.CalDave computes HMAC-SHA256(webhook_secret, raw_request_body)
2.The hex digest is sent in X-CalDave-Signature
3.Your server recomputes the HMAC over the raw body and compares
Node.js verification example
const crypto = require('crypto');

// Use the raw body string, NOT parsed JSON
const signature = req.headers['x-caldave-signature'];
const expected = crypto
  .createHmac('sha256', WEBHOOK_SECRET)
  .update(rawBody)
  .digest('hex');

const valid = crypto.timingSafeEqual(
  Buffer.from(expected),
  Buffer.from(signature)
);

if (!valid) {
  return res.status(401).json({ error: 'Invalid signature' });
}
Python verification example
import hmac, hashlib

signature = request.headers.get('X-CalDave-Signature')
expected = hmac.new(
    WEBHOOK_SECRET.encode(),
    request.data,  # raw bytes
    hashlib.sha256
).hexdigest()

if not hmac.compare_digest(expected, signature):
    abort(401)
Always verify against the raw request body (the exact bytes received), not re-serialized JSON. Use constant-time comparison (timingSafeEqual / compare_digest) to prevent timing attacks. If no webhook_secret is set, the header is omitted and payloads are unsigned.

Webhook Events

When a calendar has a webhook_url configured, CalDave automatically delivers webhooks whenever events are created, updated, deleted, or responded to — whether via the API or inbound email. Time-based webhooks (event.starting and event.ending) fire automatically when an event's start or end time arrives, polled every 60 seconds.

Event Types
event.createdA new event was added (API or inbound email)
event.updatedAn event was modified (API PATCH or inbound email update)
event.deletedAn event was deleted or cancelled (API DELETE or inbound email CANCEL)
event.respondedAn event was accepted, declined, or marked tentative
event.startingFired when an event's start time arrives (within ~60 seconds). Works for single events and recurring instances. Not fired for cancelled events.
event.endingFired when an event's end time arrives (within ~60 seconds). Works for single events and recurring instances. Not fired for cancelled events.
Payload Shape
{
  "type": "event.created",
  "calendar_id": "cal_...",
  "event_id": "evt_...",
  "event": { ... },
  "timestamp": "2026-02-17T12:00:00.000Z"
}
Webhooks are fire-and-forget. Delivery is not retried on failure. Use POST /calendars/:id/webhook/test to verify your endpoint is reachable before relying on live events.

Events

POST /calendars/:id/events Bearer token

Create an event on a calendar. Supports one-off and recurring events.

Body parameters
title requiredEvent title/summary
start requiredISO 8601 datetime, or YYYY-MM-DD for all-day events
end requiredISO 8601 datetime, or YYYY-MM-DD for all-day events (inclusive)
all_day optionalBoolean. When true, start/end must be YYYY-MM-DD and end is inclusive.
description optionalFree text (max 64KB)
metadata optionalStructured JSON payload (max 16KB)
location optionalFree text or URL
status optionalconfirmed (default), tentative, cancelled
attendees optionalArray of email addresses
recurrence optionalRFC 5545 RRULE string (e.g. FREQ=DAILY;BYDAY=MO,TU,WE,TH,FR). Alias: rrule
Example — one-off event
curl -s -X POST "https://caldave.ai/calendars/CAL_ID/events" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -d '{
    "title": "Team standup",
    "start": "2025-03-01T09:00:00-07:00",
    "end": "2025-03-01T09:15:00-07:00",
    "location": "https://meet.google.com/abc-defg-hij"
  }'
Example — recurring event
curl -s -X POST "https://caldave.ai/calendars/CAL_ID/events" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -d '{
    "title": "Daily weather email",
    "start": "2025-03-01T08:00:00-07:00",
    "end": "2025-03-01T08:05:00-07:00",
    "metadata": {"action": "send_email", "prompt": "Send weather forecast"},
    "recurrence": "FREQ=DAILY;BYDAY=MO,TU,WE,TH,FR"
  }'
Example — all-day event
curl -s -X POST "https://caldave.ai/calendars/CAL_ID/events" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -d '{
    "title": "Company Holiday",
    "start": "2025-12-25",
    "end": "2025-12-25",
    "all_day": true
  }'
All-day events use date-only strings (YYYY-MM-DD). The end date is inclusive, so start and end on the same date means a single day.
Recurring events are expanded into individual instances for the next 90 days. The response includes instances_created count.
GET /calendars/:id/events Bearer token

List events with optional filters. Returns expanded recurring event instances.

Query parameters
startFilter events starting after this datetime
endFilter events starting before this datetime
statusFilter by status (confirmed, tentative, cancelled)
limitMax results (default 50, max 200)
offsetPagination offset (default 0)
Example
curl -s "https://caldave.ai/calendars/CAL_ID/events?start=2025-03-01T00:00:00Z&limit=10" \
  -H "Authorization: Bearer YOUR_API_KEY"
GET /calendars/:id/events/:event_id Bearer token

Get a single event by ID.

Example
curl -s "https://caldave.ai/calendars/CAL_ID/events/EVT_ID" \
  -H "Authorization: Bearer YOUR_API_KEY"
PATCH /calendars/:id/events/:event_id Bearer token

Update an event. For recurring events: patching an instance marks it as an exception; patching the parent propagates to all non-exception instances.

Body parameters
titleEvent title
startNew start time (YYYY-MM-DD for all-day)
endNew end time (YYYY-MM-DD for all-day, inclusive)
all_dayToggle all-day mode on/off
descriptionFree text
metadataJSON payload
locationLocation
statusconfirmed, tentative, cancelled
attendeesArray of email addresses
recurrenceUpdated RRULE (parent only — triggers rematerialization). Alias: rrule
Example
curl -s -X PATCH "https://caldave.ai/calendars/CAL_ID/events/EVT_ID" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -d '{"title": "Updated title", "location": "Room 42"}'
DELETE /calendars/:id/events/:event_id Bearer token

Delete an event. For recurring event instances, use the mode query parameter.

Query parameters
modesingle — cancel this instance only (default)
future — cancel this and all future instances
all — delete entire series
Example
curl -s -X DELETE "https://caldave.ai/calendars/CAL_ID/events/EVT_ID?mode=single" \
  -H "Authorization: Bearer YOUR_API_KEY"
GET /calendars/:id/upcoming Bearer token

Get the next N events starting from now. Designed for agent polling.

Query parameters
limitNumber of events to return (default 5, max 50)
Example
curl -s "https://caldave.ai/calendars/CAL_ID/upcoming" \
  -H "Authorization: Bearer YOUR_API_KEY"
Response
{
  "events": [...],
  "next_event_starts_in": "PT14M30S"
}
next_event_starts_in is an ISO 8601 duration showing how long until the next event. Useful for setting poll intervals.
GET /calendars/:id/view Bearer token

Plain text table of upcoming events. Useful for quick inspection via curl or agent debugging.

Query parameters
limitNumber of events to show (default 10, max 50)
Example
curl -s "https://caldave.ai/calendars/CAL_ID/view" \
  -H "Authorization: Bearer YOUR_API_KEY"
Response (text/plain)
Work (cal_xxx)  tz: America/Denver
-----------------------------------------
TITLE          START                 ...
-----------------------------------------
Daily standup  2026-02-13 16:00:00Z  ...
-----------------------------------------
1 event(s)
POST /calendars/:id/events/:event_id/respond Bearer token

Accept or decline an inbound calendar invite.

Body parameters
response requiredaccepted, declined, or tentative
Example
curl -s -X POST "https://caldave.ai/calendars/CAL_ID/events/EVT_ID/respond" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -d '{"response": "accepted"}'

iCal Feed

GET /feeds/:calendar_id.ics Token in query param

Read-only iCalendar feed. Subscribe to this URL from Google Calendar, Apple Calendar, or any iCal-compatible app. The feed_token is returned when you create a calendar.

Example
curl -s "https://caldave.ai/feeds/CAL_ID.ics?token=FEED_TOKEN"
Add this URL to Google Calendar via "Other calendars" → "From URL". Events appear as read-only.

Inbound Email

POST /inbound/:token Token in URL

Receives forwarded emails containing .ics calendar invite attachments and creates events from them. Each calendar has a unique inbound URL (returned at creation as inbound_webhook_url). Supports Postmark and AgentMail providers.

How it works
REQUEST / PUBLISHCreates a new event (or updates if ical_uid matches). Status set to tentative.
CANCELSets matching event status to cancelled
Postmark: Set your inbound domain to forward to the webhook URL. Attachments are decoded from base64 inline.

AgentMail: Set the webhook URL in AgentMail's inbox settings. CalDave fetches .ics attachments via the AgentMail API using the calendar's agentmail_api_key.
Response (always 200)
{ "status": "created", "event_id": "evt_..." }
{ "status": "updated", "event_id": "evt_..." }
{ "status": "cancelled", "event_id": "evt_..." }
{ "status": "ignored", "reason": "..." }

Debugging

GET /errors Bearer token

List recent API errors scoped to your agent. Useful for debugging failed requests.

Query parameters
limitMax results (default 50, max 200)
routeFilter by route pattern
Example
curl -s "https://caldave.ai/errors" \
  -H "Authorization: Bearer YOUR_API_KEY"
GET /errors/:id Bearer token

Get a single error with full stack trace.

Example
curl -s "https://caldave.ai/errors/42" \
  -H "Authorization: Bearer YOUR_API_KEY"

Discovery

GET /changelog No auth (optional Bearer)

Structured list of API changes with dates and docs links. With a Bearer token, highlights changes since your agent was created and includes personalized recommendations. Poll ~weekly to discover new features.

Example
curl -s "https://caldave.ai/changelog" \
  -H "Authorization: Bearer YOUR_API_KEY"
Response (authenticated)
{
  "description": "CalDave API changelog...",
  "your_agent": { "agent_id": "agt_...", "created_at": "..." },
  "changes_since_signup": [{ "date": "2026-02-14", "changes": [...] }],
  "changes_since_signup_count": 2,
  "changelog": [{ "date": "2026-02-08", "changes": [...] }],
  "recommendations": [
    { "action": "Name your agent", "why": "...", "how": "PATCH /agents ...", "docs": "..." }
  ]
}
The recommendations array includes actionable suggestions based on your agent state (e.g. name your agent, create a calendar, create an event). Only present when authenticated and there are suggestions.
GET /man No auth (optional Bearer)

Machine-readable API manual. Returns all endpoints with curl examples and parameters. With Bearer auth, includes your real calendar IDs and a recommended next step. Use ?guide for a compact onboarding overview.

Query parameters
guideCompact onboarding overview (no value needed, just ?guide)
topicFilter to specific topic(s). Returns only matching endpoints plus discovery endpoints. Comma-separated for multiple topics.
Available topics: agents, smtp, calendars, events, feeds, errors
Example — full reference
curl -s "https://caldave.ai/man" \
  -H "Authorization: Bearer YOUR_API_KEY"
Example — guided onboarding
curl -s "https://caldave.ai/man?guide" \
  -H "Authorization: Bearer YOUR_API_KEY"
Example — topic-scoped
curl -s "https://caldave.ai/man?topic=events" \
  -H "Authorization: Bearer YOUR_API_KEY"
curl -s "https://caldave.ai/man?topic=agents,smtp" \
  -H "Authorization: Bearer YOUR_API_KEY"
The ?guide mode returns only an overview, your context, a recommended next step, and links to discover more. Ideal for first-time agent onboarding.
The ?topic= filter is useful when you only need docs for a specific area. For example, ?topic=events returns only event endpoints plus discovery. Combine topics with commas: ?topic=agents,smtp.

Quick Start

Get up and running in three steps:

1. Create an agent
curl -s -X POST "https://caldave.ai/agents" \
  -H "Content-Type: application/json" \
  -d '{"name": "My Agent", "description": "What this agent does"}'

Save the agent_id and api_key from the response. The name appears in outbound emails.

2. Create a calendar
curl -s -X POST "https://caldave.ai/calendars" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -d '{"name": "My Calendar", "timezone": "America/Denver"}'

Save the calendar_id, email, and feed URLs from the response.

3. Create an event
curl -s -X POST "https://caldave.ai/calendars/CAL_ID/events" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -d '{
    "title": "My first event",
    "start": "2025-03-01T10:00:00-07:00",
    "end": "2025-03-01T11:00:00-07:00"
  }'