OpenCal / Docs

Auth

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.

Errors

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

Syncing

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.


POST /ping

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 '{}'

POST /register

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"

POST /unregister

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

POST /invite

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
email String *

JSON feed format

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"

ICS feed format

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

to get started