CalMonkey’s v1 API follows Cronofy’s calendar API: the same paths, parameter names, response shapes, status codes and error shapes for connecting calendars, free/busy, events with attendees, recurring events and conferencing, and push notifications. An existing Cronofy client usually switches with a change of hostnames and credentials, and its users reconnecting.
These endpoints have Cronofy’s paths, parameters, response fields, status codes and error shapes. A Cronofy client that uses only them switches by changing hostnames and credentials.
GET /oauth/authorize
POST /oauth/token
POST /oauth/token/revoke
POST /v1/application_calendars
GET /v1/account
GET /v1/userinfo
GET /v1/profiles
GET /v1/calendars
GET /v1/free_busy
GET /v1/events
POST /v1/calendars/{id}/events
DELETE /v1/calendars/{id}/events
POST /v1/calendars/{id}/events/occurrences
POST /v1/channels
GET /v1/channels
DELETE /v1/channels/{id}
POST /v1/link_tokens
Provider names are Cronofy’s: google, office365, live_connect, apple. Every response also carries a Calmonkey-Request-Id header, and authenticated responses the RateLimit-* headers.
Attendees, recurring events and conferencing
Event writes and reads take Cronofy’s fields and return Cronofy’s shapes:
attendees: { invite: [{ email, display_name }], remove: [{ email }] } on writes; attendees[] with email, display_name and status, organizer and participation_status on reads. A wrong address is errors.invalid_email on attendees.invite[0].email, as Cronofy.
recurrence: { rules: [{ frequency, interval, count | until, by_day: [{ day, nth_of_period }] }] }, with one rule, a date-only until and Cronofy’s keys for what a rule cannot say (errors.not_supported, errors.not_recognized, errors.must_be_date, errors.must_be_integer_greater_than_zero, errors.not_supported_count_until_combination, errors.max_recurrence_rules_exceeded, errors.invalid_format). Leaving recurrence out keeps the series and null removes it. exceptions.add skips dates.
conferencing: { profile_id } with default, integrated and none; reads show { pending: true }, then the join_url.
Outside the compatible set
Cronofy’s other products and endpoints have no counterpart on this API: the Availability and Scheduler APIs, Smart Invites, conferencing.profile_id: "explicit" with a join URL of your own, changing a participation status through the API, on-premises Exchange, and per-profile revoke (POST /v1/profiles/{id}/revoke). Revoke the whole grant with /oauth/token/revoke instead.
Cronofy compatibility mode
Where CalMonkey’s own behaviour differs from Cronofy’s, an application can ask for Cronofy’s. Compatibility mode is a setting of each application, off by default. Turn it on for the application an existing Cronofy client will use; leave it off for new integrations. You switch it in the application’s page in the dashboard.
Compatibility mode on and off
Behaviour
Compatibility on (as Cronofy)
Off (CalMonkey)
All-day event written without transparency
transparent (free), as Cronofy
opaque (busy)
free_busy periods
event_uid only with include_ids=true
always event_uid
422 wording
“<field> must be specified”; errors.tzid_unrecognized “TZID not recognized”; errors.invalid_time_pair “end must be after start”
errors.required “required”; errors.invalid
POST /v1/application_calendars
no account_id; linking_profile.profile_name null; the same token pair again while it is valid
account_id; the calendar’s name; a new pair on every call
Refreshing a token
the same access token while it has at least 10 minutes left (expires_in = what is left)
a new access token
GET /v1/account for an application calendar
403, plain text
200
GET /v1/userinfo
cronofy.type / cronofy.data; an application calendar has application_calendar_id and no name or zoneinfo
the first occurrence carries event_id and recurrence; later ones carry series_identifier and series_master, no event_id, and options.update false
every occurrence carries event_id, series_identifier and series_master; the first also recurrence
A timed recurring event written without tzid
accepted; it repeats at the same UTC time
422 on tzid: a series repeats in local time and needs its zone
recurrence with an empty rules list
accepted: the event stops repeating
422; send recurrence: null
attendees on a calendar that sends no invitations
errors.event.cannot_set_attendees
errors.attendees_unsupported
conferencing.profile_id integrated on a calendar without a link of its own
errors.integrated_conferencing_not_available
errors.conferencing_unavailable
An unknown conferencing.profile_id
errors.not_recognized
errors.invalid_value
Webhook signatures: in compatibility mode Cronofy-HMAC-SHA256 is computed exactly as Cronofy computes it (base64 HMAC-SHA256 of the body, keyed with your client secret), so your existing verification keeps working once it uses the new client secret.
Differences in every mode
Callback URLs must be https on port 443 and on a public host. Cronofy also accepts http and other ports.
Channels have no scheduling_conversations key (that belongs to Cronofy’s Scheduler).
Profiles carry two added keys, which Cronofy clients ignore: profile_calendars (each profile’s calendars, so one call is enough) and profile_relink_url (always null: a profile is reconnected through /oauth/authorize).
Events always carry event_private (false), extended_transparency (the same as transparency), organizer, and url when one was written. Calendars carry calendar_attachments_available (false). Profiles carry provider_service.
Attendees.notify_attendees (on writes and deletes) is CalMonkey’s own field. With notify_attendees: false an application calendar stores guests, which Cronofy’s application calendars refuse; taking guests off with remove alone is always accepted. A body with a mistake in it answers with the mistake first, and with the calendar’s refusal once the body is valid.
Recurring events. The event’s start must itself be an occurrence of the rule (a rule for Tuesdays needs a start on a Tuesday, in the event’s time zone); Cronofy accepts a start that is not. A monthly rule without by_day starts on the 1st to the 28th, a yearly one on any date but 29 February. count goes up to 730 and interval to 99. start and end are also accepted as { time, tzid } objects.
Single occurrences.recurrence.exceptions.change, DELETE with occurrence_date and POST /v1/calendars/{id}/events/occurrences are CalMonkey’s own, and every occurrence carries an added occurrence_date. Other keys under exceptions (Cronofy’s exclude among them) are errors.not_supported, as on Cronofy.
Conferencing. A made link reads { provider_name, join_url }: provider_name is added. profile_id: "default" on a calendar without a link of its own (iCloud, application calendars) writes the event without one and reads show no conferencing key; Cronofy shows pending there. profile_id: "explicit" is refused with 422 on conferencing.profile_id.
Wrong client credentials are 400 {"error":"invalid_client"} on /oauth/* and 401 with an empty body on POST /v1/application_calendars, as Cronofy.
Webhooks are retried for about 23 hours (schedule); 410 Gone closes the channel.
Switching, step by step
Create an application in the dashboard. Copy the client id and the client secret (shown once).
Register your redirect URIs, exactly as your code sends them, and turn on Cronofy compatibility mode.
Point your client at CalMonkey. If it reads the hosts from settings, that is four values:
The API host replaces api.cronofy.com (or your data centre’s API host); the app host replaces app.cronofy.com for /oauth/authorize. If your code checks that callback or API URLs end in cronofy.com, change that check to compare with the CalMonkey hosts exactly.
Check webhook verification with the new client secret, and that your callback URL is https on port 443.
Move your users. Tokens cannot be carried over from Cronofy: each user connects their calendar again through CalMonkey. Before switching, for each connection: delete the events you wrote through Cronofy, close its channels and revoke its Cronofy token; then mark the connection as needing to reconnect, and show the user a short message (“Calendar sync was upgraded. Connect your calendar again.”). When they reconnect, write their upcoming events again: writes are idempotent by event_id.
Test first with an application calendar, which needs no browser, then with one real account per provider you offer.
CalMonkey is hosted in Sydney, Australia. There is no choice of data centre, so there is no data centre in the hostnames either.