You write events to a user's calendar under your own event id, for example your booking id. There is one call to create or update an event and one to delete it. You never need to store Google or Microsoft event ids.
Create or update an event
PUT /users/{user_id}/events/{event_id} creates the event the first time and updates it after that:
curl -X PUT https://api.bentocal.com/au/v1/users/user_123/events/booking_8812 \
-H "Authorization: Bearer $BENTOCAL_TEST_KEY" \
-H "Content-Type: application/json" \
-d '{
"summary": "Intro call",
"description": "Booked through Example App.",
"start": "2026-10-20T23:00:00Z",
"end": "2026-10-20T23:30:00Z",
"time_zone": "Australia/Sydney",
"location": "https://meet.example.com/abc"
}'{
"event_id": "booking_8812",
"connection_id": "conn_4kT9pQ2vXa7LmN3s",
"calendar_id": "sam@example.com",
"provider_event_id": "5q2hb7n1k8v3c0d9"
}The response is 201 when the event was created and 200 when it was updated. Because the same id always means the same event, it's safe to retry a request that timed out: you won't get a duplicate.
Event ids are yours to choose (1 to 255 characters; URL-encode them in the path) and are scoped to the user, so you can use the same booking id for each person attending.
Fields
| Field | Required | Notes |
|---|---|---|
summary | Yes | The event title, up to 1024 characters. |
start, end | Yes | ISO 8601 with an offset. end must be after start. |
description | No | Up to 8192 characters. |
location | No | Up to 1024 characters, for example a meeting link. |
time_zone | No | IANA time zone, for example Australia/Sydney. Calendars show the event in this zone. |
all_day | No | true for an all-day event. See below. |
transparency | No | opaque (default) blocks the time in free/busy; transparent doesn't. |
connection_id | No | Write to this connection instead of the user's default connection. |
calendar_id | No | Write to this calendar instead of the connection's primary calendar. |
BentoCal writes the event to the user's own calendar only. It doesn't invite attendees: to put a meeting in several people's calendars, write it once for each user.
Which calendar it goes to
By default an event goes to the primary calendar of the user's default connection. The default is set when the user first connects a calendar, and you can change it with PATCH /users/{user_id}.
Pass connection_id and/or calendar_id to choose another calendar. Use GET /connections/{connection_id}/calendars to find calendar ids; only calendars with can_write: true accept events.
If a later write for the same event id targets a different calendar, BentoCal moves the event there. If the user deleted the event in their calendar app, the next write creates it again.
All-day events
With all_day: true, the event covers whole days in time_zone (UTC if you leave it out):
- It starts on the date of
start. - An
endat exactly local midnight is exclusive:2026-10-20T00:00to2026-10-21T00:00is one day, the 20th. - An
endat any other time includes that day. - An all-day event always covers at least one day.
{
"summary": "Conference",
"all_day": true,
"start": "2026-10-20T00:00:00+11:00",
"end": "2026-10-22T00:00:00+11:00",
"time_zone": "Australia/Sydney"
}This is a two-day event on 20 and 21 October.
Delete an event
DELETE /users/{user_id}/events/{event_id} removes the event from the calendar and returns 204.
Deleting is idempotent: an event id BentoCal doesn't know, or an event already deleted in the calendar, is also a success. If the connection needs relinking, the call fails with 409 relink_required and BentoCal keeps track of the event, so you can delete it once the user reconnects.
Errors
| Status | Code | Cause |
|---|---|---|
| 404 | not_found | The user, connection or calendar doesn't exist. |
| 409 | relink_required | The connection needs relinking. details.connection_id says which. See Relink a connection. |
| 409 | conflict | The user has no default connection, or the connection was revoked. |
| 403 | forbidden | The provider refused the write, for example a read-only calendar. |
| 502 | provider_unavailable | Google or Microsoft failed. Safe to retry after Retry-After seconds. |
See Errors for the full list.