Cal.diy calendars and free/busy
Use these capabilities to inspect connected calendars and availability, work with events, choose the destination for new bookings, or change conflict detection. This page owns all 21 operations in the calendar family.
All operations use provider API version 2024-08-13. Every input and result in
this family is restricted, contains PII, and requires redaction.
Choose a calendar surface
Cal.diy exposes two event surfaces. Choose one before constructing input:
| Surface | Selector | Provider scope |
|---|---|---|
| Unified connection | Integer connection_id; optional calendarId |
A connection returned by Cal.diy |
| Direct provider | calendar fixed to google |
Google event and free/busy routes only |
The broader provider checks accept google, office365, or apple. That does
not expand the direct event surface: its published schema still requires
"calendar": "google".
Choose an operation
Connection and availability reads
| Capability | Provider request | Input |
|---|---|---|
cap:cal_diy:calendar.list |
GET /v2/calendars |
Empty object |
cap:cal_diy:calendar.provider.check |
GET /v2/calendars/{calendar}/check |
Provider |
cap:cal_diy:calendar.ics_feed.check |
GET /v2/calendars/ics-feed/check |
Empty object |
cap:cal_diy:calendar.busy_time.list |
GET /v2/calendars/busy-times |
Range and calendars |
cap:cal_diy:calendar.connection.list |
GET /v2/calendars/connections |
Empty object |
cap:cal_diy:calendar.connection.free_busy.get |
GET /v2/calendars/connections/{connection_id}/freebusy |
Connection and range |
cap:cal_diy:calendar.free_busy.get |
GET /v2/calendars/{calendar}/freebusy |
Google and range |
All seven operations are low-risk, approval-free reads at the capability contract level.
Unified connection events
| Capability | Provider request | Risk | Approval |
|---|---|---|---|
cap:cal_diy:calendar.connection.event.list |
GET /v2/calendars/connections/{connection_id}/events |
Low | No |
cap:cal_diy:calendar.connection.event.get |
GET /v2/calendars/connections/{connection_id}/events/{event_id} |
Low | No |
cap:cal_diy:calendar.connection.event.create |
POST /v2/calendars/connections/{connection_id}/events |
Medium | Required |
cap:cal_diy:calendar.connection.event.update |
PATCH /v2/calendars/connections/{connection_id}/events/{event_id} |
Medium | Required |
cap:cal_diy:calendar.connection.event.delete |
DELETE /v2/calendars/connections/{connection_id}/events/{event_id} |
High | Required |
Direct Google events
| Capability | Provider request | Risk | Approval |
|---|---|---|---|
cap:cal_diy:calendar.event.list |
GET /v2/calendars/{calendar}/events |
Low | No |
cap:cal_diy:calendar.event.get |
GET /v2/calendars/{calendar}/events/{event_uid} |
Low | No |
cap:cal_diy:calendar.event.create |
POST /v2/calendars/{calendar}/events |
Medium | Required |
cap:cal_diy:calendar.event.update |
PATCH /v2/calendars/{calendar}/events/{event_uid} |
Medium | Required |
cap:cal_diy:calendar.event.delete |
DELETE /v2/calendars/{calendar}/events/{event_uid} |
High | Required |
For every direct operation in this table, calendar must equal google.
Routing and conflict detection
| Capability | Provider request | Risk | Approval |
|---|---|---|---|
cap:cal_diy:calendar.disconnect |
POST /v2/calendars/{calendar}/disconnect |
High | Required |
cap:cal_diy:calendar.destination.update |
PUT /v2/destination-calendars |
Medium | Required |
cap:cal_diy:calendar.selected.add |
POST /v2/selected-calendars |
Medium | Required |
cap:cal_diy:calendar.selected.delete |
DELETE /v2/selected-calendars |
High | Required |
Disconnect, both event deletes, and selected-calendar delete are destructive.
Inspect calendar connections
List connected calendars with an empty input:
{}
Use calendar.provider.check to test one provider connection:
{
"calendar": "google"
}
The exact provider enum is google, office365, or apple. Provider check
shows whether the authenticated integration is connected and healthy at that
request boundary. It does not prove that every calendar is visible or writable.
calendar.ics_feed.check also accepts an empty object and checks the
authenticated user’s ICS feed connection. calendar.connection.list returns
credential-safe unified connection identifiers; the connector does not publish
their provider-owned response fields.
These reads inspect connections that already exist. Provider connect and save routes that establish browser OAuth sessions or credentials remain outside the agent-callable business-operation surface.
Read busy time across selected calendars
calendar.busy_time.list requires a time zone, a bounded range, and at least
one explicit calendar:
{
"timeZone": "UTC",
"dateFrom": "2026-08-03",
"dateTo": "2026-08-04T18:00:00Z",
"calendarsToLoad": [
{
"credentialId": 42,
"externalId": "primary@example.com"
}
]
}
| Field | Constraint |
|---|---|
timeZone |
Non-empty string |
dateFrom, dateTo |
ISO date or date-time string |
calendarsToLoad |
Array with at least one object |
credentialId |
Integer required in each object |
externalId |
Non-empty string required in each object |
The client sends these values as query parameters. Nested calendar objects use
bracket notation such as calendarsToLoad[0][credentialId]. The schema does not
set a maximum or require unique calendar objects, and it does not assert that
dateFrom precedes dateTo.
Busy intervals are scheduling evidence, not a reservation or a guarantee that a later booking will succeed.
Read unified connection events
List events for a unified connection with a required range:
{
"connection_id": 73,
"from": "2026-08-03T00:00:00Z",
"to": "2026-08-04T00:00:00Z",
"timeZone": "UTC",
"calendarId": "team@example.com"
}
connection_id, from, and to are required. from and to accept either
an ISO date or date-time. Optional timeZone and calendarId must be non-empty.
The connection id is a path segment; all range fields and calendarId become
query parameters.
Read one event with connection_id and a non-empty event_id. An optional
calendarId selects a calendar within that connection and is sent as a query
parameter:
{
"connection_id": 73,
"event_id": "provider-event-uid",
"calendarId": "team@example.com"
}
Create a unified connection event
Create requires the connection, title, start, and end:
{
"connection_id": 73,
"calendarId": "team@example.com",
"title": "Architecture review",
"start": {
"time": "2026-08-03T10:00:00Z",
"timeZone": "UTC"
},
"end": {
"time": "2026-08-03T10:30:00Z",
"timeZone": "UTC"
},
"description": "Review the proposed integration boundary.",
"attendees": [
{
"email": "avery@example.com",
"name": "Avery Patel"
}
]
}
title must be non-empty. Both start and end require a date-time time
and a non-empty timeZone. description may be a string or null.
attendees may contain objects with a required email and optional name;
the array has no published minimum or uniqueness constraint. Additional fields
are rejected at the event, date-time, and attendee object boundaries.
calendarId is optional. The client puts it in the query while sending event
details as JSON. The connector contract carries write and external_network,
but not send_message. Attendee input is therefore not evidence that the
provider sent an invitation.
The create operation names calendar.connection.event.delete as best-effort
compensation. Compensation requires a separate approval and is not automatic.
Update or delete a unified connection event
Update requires connection_id and event_id. It may include calendarId
and any of these body fields:
| Field | Constraint |
|---|---|
title |
String; an empty string passes the connector schema |
start, end |
Object with optional date-time time and string timeZone |
description |
String or null |
attendees |
Array or null |
status |
accepted, pending, declined, cancelled, or null |
An attendee update requires email and may include name, self, optional,
host, and responseStatus. Response status accepts accepted, pending,
declined, needsAction, or null.
The schema allows an identifier-only update and empty start or end objects.
Such input can pass local validation without expressing a useful provider
change. Include only an intentional, provider-supported patch.
Delete uses connection_id and event_id, plus optional calendarId. The two
ids become path segments and calendarId becomes a query parameter. Delete is
high risk and has no rollback contract.
Both update and delete first read the exact unified event. A successful preflight proves only that the event was readable before mutation.
Read, create, or change a direct Google event
The direct surface mirrors the unified event model with these selector changes:
- every input requires
"calendar": "google"; - list also requires
fromandto, with optionaltimeZoneandcalendarId; - get and delete require a non-empty
event_uid; - create has no
calendarIdfield; - update requires
event_uidand uses the shared optional patch fields.
Create and update use the field constraints described for unified events.
Direct create names calendar.event.delete as best-effort compensation.
Direct update and delete first run calendar.event.get for the same
calendar and event_uid.
The direct schema does not publish Office 365 or Apple event variants. Use a unified connection when its returned identifier is the intended routing model.
Read free/busy
Unified free/busy requires connection_id, from, and to. Direct free/busy
requires calendar fixed to google, from, and to. Both accept optional
non-empty timeZone; all range fields are query parameters.
{
"connection_id": 73,
"from": "2026-08-03",
"to": "2026-08-04",
"timeZone": "UTC"
}
The input schema accepts dates or date-times and does not enforce ordering or a maximum interval. A free/busy response is provider-owned and restricted. It does not by itself apply schedule rules, event-type buffers, reservations, or booking policy.
Change the destination calendar
calendar.destination.update changes where new bookings are written:
{
"integration": "google_calendar",
"externalId": "primary@example.com"
}
integration must be apple_calendar, google_calendar, or
office365_calendar. externalId is required and non-empty. An optional
non-empty delegationCredentialId selects delegated provider credentials.
The connector first lists calendars. This does not prove that the requested
externalId appeared in that result; provider validation still decides the
mutation. The operation is medium risk and has no compensating capability.
Add or remove a conflict calendar
Add requires an integration name, external calendar id, and integer credential id:
{
"integration": "google_calendar",
"externalId": "team@example.com",
"credentialId": 42
}
All three fields are required. integration and externalId must be
non-empty; credentialId is an integer. Optional delegationCredentialId must
be non-empty. The schema does not constrain integration to the destination
calendar enum.
Remove uses the same route but requires credentialId as a non-empty string,
not an integer. The client sends integration, externalId, credentialId,
and optional delegationCredentialId as query parameters on DELETE.
Both operations first list calendars. Add declares
calendar.selected.delete as best-effort compensation. Remove is destructive
and has no further rollback.
Disconnect a provider credential
Disconnect requires a provider and integer credential id:
{
"calendar": "office365",
"id": 42
}
calendar must be google, office365, or apple. The client sends a POST
to the provider-specific disconnect path and leaves id in the JSON body.
Before dispatch, it checks that provider connection without using the id.
Integer identifiers in this family have no additional minimum in the published schema. Cal.diy can still reject a value outside the provider’s domain.
The operation is high risk and destructive. It deletes an owned credential and invalidates the connection. No compensation or automatic credential recovery is declared.
Preflight reads
| Mutation | Connector preflight |
|---|---|
| Unified or direct event create | calendar.list |
| Destination update | calendar.list |
| Selected-calendar add or delete | calendar.list |
| Unified event update or delete | Exact unified event get |
| Direct event update or delete | Exact direct event get |
| Calendar disconnect | Provider check for calendar |
A preflight is a point-in-time read, not a lock, plan, reservation, or approval.
Shared read contract
All 11 reads are low risk, synchronous, retry-safe, and eligible for connector retry. Their idempotency key is optional, action-scoped, and collision-checked against the input hash. No capability-level approval is required.
Identity, tenant, external-account, credential-scope, and restricted-data policy still apply to every read.
Shared mutation contract
All ten mutations require:
| Contract field | Value |
|---|---|
| Human approval | Required |
| Approval selector | Tenant policy |
| Approval validity window | 900,000 ms |
| Approval evidence | Reason, input snapshot, policy decision |
| Idempotency | Required; external-account scope |
| Collision behavior | Revalidate input hash |
| Connector retry | Not supported |
| Retry safety | Unsafe |
| Execution | Synchronous only |
| Transaction modes | dry_run, plan, commit, reconcile |
| Plan before commit | Not required |
| Dry-run fidelity | Policy and schema only |
Six non-destructive mutations are medium risk. Calendar disconnect, both event
deletes, and selected-calendar delete are high risk and destructive. None of
the ten mutations carries the send_message side-effect marker.
Event create on either surface and selected-calendar add each declare one best-effort delete compensation. All other mutations declare rollback as not supported. Never interpret a compensation declaration as automatic execution or restoration of invitations, notifications, external state, or history.
Result and redaction contract
The connector returns the validated provider JSON body as AIP output. A
non-empty string status is required. data, pagination, and additional
provider fields are optional; their shapes are not guaranteed by the connector
schema.
The connector recursively replaces secret-, key-, and token-named values with
[REDACTED]. This targeted redaction does not remove event titles, attendee
addresses, calendar ids, busy intervals, descriptions, or other PII unless a
field name matches the secret redactor. Keep the complete output restricted.
Failures and recovery
| Failure | Relevant cause | Safe response |
|---|---|---|
connector.cal_diy.invalid_action |
Missing selector, invalid range shape, invalid enum, or unknown field | Correct the exact input before another call |
connector.cal_diy.policy |
Mutation omitted its required idempotency key | Preserve approved input and add the missing key |
connector.cal_diy.idempotency_collision |
One key owns different mutation input | Stop and inspect the original action |
connector.cal_diy.in_flight |
A matching calendar mutation is executing | Read durable state and wait |
connector.cal_diy.outcome_unknown |
Prior provider dispatch cannot be proved | Reconcile; do not create a replacement key |
connector.cal_diy.authentication |
Provider returned 401 or 403 |
Verify account binding, credential revision, and calendar ownership |
connector.cal_diy.state_conflict |
Provider returned 409 or 412 |
Refresh event or calendar state and review the mutation |
connector.cal_diy.rate_limited |
Provider returned 429 |
Retry reads within policy; reconcile mutations |
connector.cal_diy.remote_temporary |
Provider returned a server error | Retry reads within policy; reconcile mutations |
connector.cal_diy.transport |
Provider exchange failed | Retry reads only; treat dispatched mutations as uncertain |
connector.cal_diy.invalid_provider_output |
Successful response lacked valid status |
Preserve evidence and reconcile a mutation |
Permanent rejection, invalid credentials, oversized responses, and durable settlement failures use the common Cal.diy error family. Gateway and lifecycle errors are described in the global error reference.
Verify calendar handling
For a read, retain the capability, exact selector and range, actor, tenant, external account, provider status, and provider request id when present. Verify that the result remains inside its approved restricted-data destination.
For a mutation, also retain approval evidence and the original idempotency key. Wait for a completed durable result, then read the exact event or list calendar state. For an uncertain dispatch, reconcile the original operation before any new action.
These checks validate application handling. They do not prove invitation delivery, provider-wide visibility, live availability, cross-provider qualification, or production readiness.