OpenCal uses API keys to authenticate requests. You can manage API keys in your account.
Authentication to the API is performed via the HTTP Authorization header. Provide your API key as the bearer value; for example:
curl https://opencal.dev/ping \
-H "Authorization: Bearer $OPENCAL_KEY" \
-H "Content-Type: application/json" \
-d '{}'
Note that $OPENCAL_KEY
is a stand-in for your actual API key.
The OpenCal API attempts to provide structured error responses, including a 4xx HTTP status code and a JSON-encoded description in the body.
When these occur, OpenCal aborts the entire operation, so that it is safe to retry.
All endpoints respond with an empty JSON object {}
on success.
Structured error response bodies will always include a error
property summarizing the failure.
Certain errors also include a hint
property with a human-readable description.
Here are a few examples, followed by a complete reference table:
{"error": "request_auth"}
{"error": "request_body", "hint": "859: unexpected token at '{'"}
{"error": "request_body", "hint": "Expected String at $.id: Received 123"}
Status | Error | Description |
---|---|---|
401 | request_auth | API key is invalid or expired |
402 | account_limit | You must upgrade to create more resources |
422 | request_body | Request body JSON does not match the expected format |
422 | request_id | Request body references a nonexistent resource |
422 | request_constraint | Request attempts to reuse a resource id, which must be unique |
421 | downstream_fetch | Downstream request to your feed failed |
421 | downstream_external | Downstream request to Google Calendar failed |
Once you POST /register your calendar feed, OpenCal will attempt to sync it every 15 minutes. Any issues encountered when fetching/parsing your feed will appear in your account. Calendar feeds with continual errors, may be skipped on later sync intervals.
Use this endpoint to test that your account is configured correctly. It validates your API key and confirms that OpenCal can access your Google account.
curl https://opencal.dev/ping \
-H "Authorization: Bearer $OPENCAL_KEY" \
-H "Content-Type: application/json" \
-d '{}'
Use this endpoint to start syncing a new calendar feed.
First it will validate that your request body, including uniqueness of your id
property.
Then it will fetch and validate the calendar data at your url
property.
If all validations pass, OpenCal creates an new, empty calendar on Google, which will be filled on the next sync.
curl https://opencal.dev/register \
-H "Authorization: Bearer $OPENCAL_KEY" \
-H "Content-Type: application/json" \
-d '{
"id": "your-calendar-id",
"url": "https://example.com/your-calendar",
"format": "json"
}'
Key | Type | Description |
---|---|---|
id | String * | Unique value for each calendar |
url | String * | |
format | String * | "json" | "ics" |
Use this endpoint to stop syncing a registered calendar feed. It deletes the calendar feed from OpenCal and removes the associated calendar in Google.
curl https://opencal.dev/unregister \
-H "Authorization: Bearer $OPENCAL_KEY" \
-H "Content-Type: application/json" \
-d '{
"id": "your-calendar-id"
}'
Key | Type | Description |
---|---|---|
id | String * | Unique value for each calendar |
Use this endpoint to share this calendar with another Google account.
It adds a read-only calendar permission for the user specified by the email
property and notifies them of the invite.
curl https://opencal.dev/invite \
-H "Authorization: Bearer $OPENCAL_KEY" \
-H "Content-Type: application/json" \
-d '{
"id": "your-calendar-id",
"email": "user@gmail.com"
}'
Key | Type | Description |
---|---|---|
id | String * | Unique value for each calendar |
String * |
Every time OpenCal syncs your feed, it will diff the event list against the last seen version. New events will be added to Google Calendar, changed events updated, and removed events deleted. Here is an example valid feed response, followed by a complete reference table:
{
"title": "Sample calendar",
"events": [
{
"summary": "Sample event",
"id": "internal-event-id",
"start": "2022-10-25T02:00:00Z",
"end": "2022-10-25T03:30:00Z",
"description": "Markup <b>allowed</b>!",
"conference_id": "stable-conference-id",
"recurrence": [
"RRULE:FREQ=DAILY;UNTIL=20221129;INTERVAL=3",
"RDATE;VALUE=DATE:20221108,20221122",
"EXDATE;VALUE=DATE:20221101"
],
"attendees": [
{ "email": "invite-recipient@example.com" }
]
}
]
}
Key path | Type | Description |
---|---|---|
$.title | String | |
$.events | [Object] * | |
$.events[].summary | String * | |
$.events[].id | String * | Unique value for each event |
$.events[].start | String * | ISO8601-formatted Date or DateTime |
$.events[].end | String | ISO8601-formatted Date or DateTime |
$.events[].description | String | HTML or plain text |
$.events[].transparency | String | "opaque" | "transparent" |
$.events[].location | String | |
$.events[].conference_id | String | |
$.events[].recurrence | [String] | RRULE, RDATE or EXDATE properties |
$.events[].attendees | [Object] | |
$.events[].attendees[].email | String * | For invite notifications |
$.events[].attendees[].response | String | "accepted" | "rejected" | "tentative" |
Ths JSON feed format is strongly recommended. It's simpler and provides better error messaging. Internally, OpenCal supports ICS/ICAL by first mapping it to JSON:
VCALENDAR X-WR-CALNAME | $.title |
VEVENT UID | $.events[].id |
VEVENT SUMMARY | $.events[].summary |
VEVENT DTSTART | $.events[].start |
VEVENT DTEND | $.events[].end |
VEVENT DURATION | $.events[].end (calculated, unless DTEND provided) |
VEVENT DESCRIPTION | $.events[].description |
VEVENT TRANSP | $.events[].transparency |
VEVENT LOCATION | $.events[].location |
VEVENT RRULE | $.events[].recurrence |
VEVENT RDATE | $.events[].recurrence |
VEVENT EXDATE | $.events[].recurrence |