Controlling Available Periods with a sub calendar

Required plan: Emerging

Available Periods are part of Cronofy’s Managed Availability suite of features. They allow you to record specific times that user’s are available and have them automatically included whenever that user is included in an Availability query.

One option for allowing users to control their availablity is to link Available Periods to events in a sub calendar. We’re not currently recommending this as an approach but several customers are experimenting with this so we thought we’d share how to approach it.

1. Create a Sub Calendar #

First, you will need the profile_id for the calendar account to create the calendar in.

curl -v -G --header "Authorization: Bearer {ACCESS_TOKEN}" \
  -d 'tzid=Etc/UTC' https://api.cronofy.com/v1/profiles
cronofy = Cronofy::Client.new(access_token: '{ACCESS_TOKEN}')
profiles = cronofy.list_profiles

Profiles docs

Then you can create the calendar in that profile.

curl -v --header "Authorization: Bearer {ACCESS_TOKEN}" \
    --header "Content-Type: application/json" \
    --data "{\"profile_id\": \"{PROFILE_ID}\",\"name\": \"Availability\"}" \
    https://api.cronofy.com/v1/calendars
cronofy = Cronofy::Client.new(access_token: '{ACCESS_TOKEN}')
calendar = cronofy.create_calendar("{PROFILE_ID}", "Availability")

calendar_id = calendar.calendar_id

Track the calendar_id of the new calendar as you’ll need that for the next step.

Create Calendar docs

2. Setup Push Notification Channel #

You now need to create a channel to receive notications of any changes to events in that calendar.

The callback_url can be any public URL that Cronofy can post to. You will want to include any parameters in this URL to allow you to identify the user it relates to, eg a user id or similar. This will allow you to look up the access_token for the user to perform the updates required when ever something changes.

curl -v --header "Authorization: Bearer {ACCESS_TOKEN}" \
    --header "Content-Type: application/json" \
    --data "{\"callback_url\": \"{CALLBACK_URL}\",\"filters\": { \"calendar_ids\": [ \"{CALENDAR_ID}\" }}" \
    https://api.cronofy.com/v1/channels
cronofy = Cronofy::Client.new(access_token: '{ACCESS_TOKEN}')
cronofy.create_channel("{CALLBACK_URL}", { calendar_ids: [ calendar_id ] })

Push Notification docs

3. Process Change Notifications #

The standard pattern for processing notifications comes into play here. Your app will receive notifications similar to the following.

POST {CALLBACK_URL_PATH} HTTP/1.1
Host: {CALLBACK_URL_HOST}
Content-Type: application/json; charset=utf-8

{
  "notification": {
    "type": "change",
    "changes_since": "2024-07-27T07:24:16Z"
  },
  "channel": {
    "channel_id": "chn_54cf7c7cb4ad4c1027000001",
    "callback_url": "{CALLBACK_URL}",
    "filters": {}
  }
}

The key value here is the changes_since parameter. You pass this to the Read Events endpoint as the last_modified parameter to retrieve the events that have changed.

curl -v -G --header "Authorization: Bearer {ACCESS_TOKEN}" \
  -d 'tzid=Etc/UTC' https://api.cronofy.com/v1/events?last_modified={CHANGES_SINCE}&calendar_ids[]={CALENDAR_ID}&include_deleted=1&include_moved=1
cronofy = Cronofy::Client.new(access_token: '{ACCESS_TOKEN}')
options = {
  last_modified: {CHANGES_SINCE},
  calendar_ids: [ {CALENDAR_ID} ],
  include_deleted: true,
  include_moved: true
}
events = cronofy.read_events(options)

You then iterate over the events and either upsert or delete Available Periods.

events.each do |event|
  if event.deleted
    cronofy.delete_available_period(event.event_uid)
  else
    period = {
      available_period_id: event.event_uid,
      start: event.start,
      end: event.end
    }
    cronofy.upsert_available_period(period)
  end
end

Because Cronofy is using an upsert pattern that allows you to specify the available_period_id, just using the event.event_uid value means there is no state to be stored. Your code can just deal with each change as it appears.

Available Periods docs

4. Use in Availability Queries #

You can then enable the Managed Availability features for any user you have set up for this kind availability tracking.