Calendly Custom Objects Example

This config showcases how to define a CX app that uses Custom Objects. One object must be defined with ac_object set to "Contact" to map any contact data to the core Contact. One or more objects can be defined without ac_object and will be stored as records bound to the associated Contact.

To learn how to define these objects in detail, please refer to our Custom Objects reference.

{
  "$version": "2",
  "api": {
    "base_url": "https://calendly.com/api/v1",
    "pagination": {
      "style": {
        "type": "page",
        "page_number": {
          "param": "page",
          "value": 1
        },
        "page_size": {
          "param": "pageSize",
          "value": 50
        }
      },
      "parser": {
        "total_pages": {
          "!jq": ".page_count"
        }
      }
    }
  },
  "auth": {
    "calendly-oauth": {
      "type": "oauth2",
      "configuration": {
        "authorization_base_url": "https://calendly.com/oauth/authorize",
        "client_id": "",
        "client_secret": "",
        "refresh_url": "https://calendly.com/oauth/token",
        "scopes": [],
        "token_url": "https://calendly.com/oauth/token"
      }
    }
  },
  "objects": [
    {
      "name": "user",
      "labels": {
        "singular": "Invitee",
        "plural": "Invitees"
      },
      "description": "Calendly Invitee",
      "ac_object": "Contact",
      "external_id": "id",
      "fields": []
    },
    {
      "name": "event",
      "labels": {
        "singular": "Calendly Event",
        "plural": "Calendly Events"
      },
      "description": "A Calendly event",
      "transform": {
        "event_type": ".payload.event_type.name",
        "start_time": ".payload.event.start_time",
        "location": ".payload.event.location",
        "url": "@uri \"https://calendly.com/app/scheduled_events/user/me?period=fixed&status_ids%5B%5D=all&start_date=\\(.payload.event.start_time | split(\"T\")[0])&end_date=\\(.payload.event.end_time | split(\"T\")[0])&invitee_emails%5B%5D=\\(.payload.invitee.email)\"",
        "status": "if .event == \"invitee.created\" then \"Scheduled\" elif .payload.invitee.is_reschedule then \"Rescheduled\" else \"Canceled\" end",
        "invitee_first_name": ".payload.invitee.first_name",
        "invitee_last_name": ".payload.invitee.last_name",
        "invitee_email": ".payload.invitee.email",
        "invitee_created_at": ".payload.invitee.created_at",
        "invitee_text_reminder_number": ".payload.invitee.text_reminder_number",
        "invitee_time_zone": ".payload.invitee.timezone",
        "assigned_to": "[.payload.event.extended_assigned_to[].name] | join(\", \")",
        "start_date_only": ".payload.event.start_time | split(\"T\")[0]",
        "start_time_only": ".payload.event.start_time | split(\"T\")[1] | split(\"-\")[0]",
        "id": ".payload.event.uuid",
        "end_time": ".payload.event.end_time",
        "end_date_only": ".payload.event.end_time | split(\"T\")[0]",
        "end_time_only": ".payload.event.end_time | split(\"T\")[1] | split(\"-\")[0]",
        "kind": ".payload.event_type.kind",
        "duration": ".payload.event_type.duration",
        "created_at": ".payload.event.created_at",
        "is_canceled": ".payload.event.canceled",
        "is_reschedule": "if .payload.old_invitee? then .payload.old_invitee else .payload.invitee end | .is_reschedule"
      },
      "relationships": [
        {
          "description": "Invitee of the event",
          "object": "user",
          "has_many": false,
          "field": ".invitee.uuid",
          "type": "data",
          "id": "user",
          "labels": {
            "singular": "User",
            "plural": "Users"
          }
        }
      ],
      "fields": [
        {
          "id": "event_type",
          "labels": {
            "singular": "Event Type",
            "plural": "Event Types"
          },
          "description": "The name of the Calendly event type",
          "type": "text",
          "is_required": false
        },
        {
          "id": "start_time",
          "labels": {
            "singular": "Event Date & Time",
            "plural": "Event Date & Times"
          },
          "description": "The date and time of the event",
          "type": "datetime",
          "is_required": false
        },
        {
          "id": "location",
          "labels": {
            "singular": "Event Location",
            "plural": "Event Locations"
          },
          "description": "The location of the event",
          "type": "text",
          "is_required": false
        },
        {
          "id": "url",
          "labels": {
            "singular": "Event Details",
            "plural": "Event Details"
          },
          "description": "Link to the event in Calendly",
          "type": "text",
          "is_required": false
        },
        {
          "id": "status",
          "labels": {
            "singular": "Status",
            "plural": "Statuses"
          },
          "description": "The status of the event",
          "type": "text",
          "is_required": false
        },
        {
          "id": "invitee_first_name",
          "labels": {
            "singular": "First Name",
            "plural": "First Names"
          },
          "description": "The first name of the event invitee",
          "type": "text",
          "is_required": false
        },
        {
          "id": "invitee_last_name",
          "labels": {
            "singular": "Last Name",
            "plural": "Last Names"
          },
          "description": "The last name of the event invitee",
          "type": "text",
          "is_required": false
        },
        {
          "id": "invitee_email",
          "labels": {
            "singular": "Email",
            "plural": "Emails"
          },
          "description": "The email of the event invitee",
          "type": "text",
          "is_required": false
        },
        {
          "id": "invitee_created_at",
          "labels": {
            "singular": "Invitee Created At",
            "plural": "Invitee Created Ats"
          },
          "description": "The date the invitee was created",
          "type": "datetime",
          "is_required": false
        },
        {
          "id": "invitee_text_reminder_number",
          "labels": {
            "singular": "Invitee Text Reminder Number",
            "plural": "Invitee Text Reminder Numbers"
          },
          "description": "The SMS reminder number for the invitee",
          "type": "text",
          "is_required": false
        },
        {
          "id": "invitee_time_zone",
          "labels": {
            "singular": "Invitee Timezone",
            "plural": "Invitee Timezones"
          },
          "description": "The timezone of the invitee",
          "type": "text",
          "is_required": false
        },
        {
          "id": "assigned_to",
          "labels": {
            "singular": "Assigned To",
            "plural": "Assigned Tos"
          },
          "description": "The user(s) the event is assigned to",
          "type": "text",
          "is_required": false
        },
        {
          "id": "start_date_only",
          "labels": {
            "singular": "Event Date",
            "plural": "Event Dates"
          },
          "description": "The start date of the event",
          "type": "date",
          "is_required": false
        },
        {
          "id": "start_time_only",
          "labels": {
            "singular": "Event Time",
            "plural": "Event Times"
          },
          "description": "The start time of the event",
          "type": "text",
          "is_required": false
        },
        {
          "id": "id",
          "labels": {
            "singular": "Id",
            "plural": "Ids"
          },
          "description": "Calendly system ID for the event",
          "type": "text",
          "is_required": false
        },
        {
          "id": "end_time",
          "labels": {
            "singular": "Event End Date & Time",
            "plural": "Event End Date & Times"
          },
          "description": "Date and time the event ends",
          "type": "datetime",
          "is_required": false
        },
        {
          "id": "end_date_only",
          "labels": {
            "singular": "Event End Date",
            "plural": "Event End Dates"
          },
          "description": "Date the event ends",
          "type": "date",
          "is_required": false
        },
        {
          "id": "end_time_only",
          "labels": {
            "singular": "Event End Time",
            "plural": "Event End Times"
          },
          "description": "Time the event ends",
          "type": "text",
          "is_required": false
        },
        {
          "id": "kind",
          "labels": {
            "singular": "Event Kind",
            "plural": "Event Kinds"
          },
          "description": "The kind of event (solo or group)",
          "type": "text",
          "is_required": false
        },
        {
          "id": "duration",
          "labels": {
            "singular": "Event Duration",
            "plural": "Event Durations"
          },
          "description": "The duration of the event in minutes",
          "type": "number",
          "scale": 0,
          "is_required": false
        },
        {
          "id": "created_at",
          "labels": {
            "singular": "Event Created At",
            "plural": "Event Created Ats"
          },
          "description": "The date the event was created",
          "type": "datetime",
          "is_required": false
        },
        {
          "id": "is_canceled",
          "labels": {
            "singular": "Event Canceled",
            "plural": "Event Canceleds"
          },
          "description": "Whether the event has been canceled",
          "type": "text",
          "is_required": false
        },
        {
          "id": "is_reschedule",
          "labels": {
            "singular": "Is Reschedule",
            "plural": "Is Reschedules"
          },
          "description": "Whether the event is a reschedule of a previous event",
          "type": "text",
          "is_required": false
        }
      ],
      "external_id": ".payload.event.uuid"
    }
  ],
  "data_intake": [
    {
      "name": "calendly_webhook",
      "type": "webhook",
      "scope": "connection",
      "resource_id": {
        "!jq": ".payload.event_type.uuid"
      },
      "create": {
        "!pipe": [
          {
            "!http": {
              "body": {
                "url": "${webhook.url}",
                "events": [
                  "invitee.created",
                  "invitee.canceled"
                ]
              },
              "headers": {
                "content-type": "application/json"
              },
              "method": "POST",
              "path": "/hooks"
            }
          },
          {
            "!jq": "{webhook_id: .id}"
          },
          {
            "!save": {
              "scope": "connection"
            }
          }
        ]
      },
      "delete": {
        "!http": {
          "method": "DELETE",
          "path": "/hooks/${data.connection.webhook_id}"
        }
      }
    }
  ],
  "workflows": [
    {
      "name": "calendly-create-a-contact",
      "label": "Create or Update Contacts from Calendly",
      "type": "generic",
      "auth": "calendly-oauth",
      "resource_type": {
        "singular": "meeting",
        "plural": "meetings"
      },
      "data_intake": "calendly_webhook",
      "setup": {
        "connect": {
          "label": "Connect",
          "describe_connection": {
            "!pipe": [
              {
                "!http": {
                  "method": "GET",
                  "path": "/users/me"
                }
              },
              {
                "!jq": "{account_id: .data.id, description: .data.attributes | (.name + \" - \" + .email)}"
              }
            ]
          }
        },
        "select": {
          "label": "Select an Event",
          "description": "Selecting an event will update Contact and Meeting data whenever that event occurs. Calendly will create a Meeting object within ActiveCampaign.",
          "form_fields": [
            {
              "label": "Choose an Event",
              "type": "dropdown",
              "id": "event",
              "placeholder": "Select Event",
              "options": {
                "!pipe": [
                  {
                    "!http": {
                      "method": "GET",
                      "params": {
                        "page_size": 100
                      },
                      "path": "/users/me/event_types"
                    }
                  },
                  {
                    "!jq": ".data | [.[] | {display: .attributes.name, value: .id}]"
                  }
                ]
              }
            }
          ],
          "describe_selection": {
            "resource_id": {
              "!jq": "${custom_data.event.value}"
            },
            "display": {
              "!jq": "${custom_data.event.display}"
            }
          }
        },
        "map": {
          "label": "Mapping",
          "describe_source": {
            "label": "Calendly",
            "options": {
              "!pipe": [
                {
                  "!http": {
                    "method": "GET",
                    "path": "https://api.calendly.com/event_types/${custom_data.event.value}"
                  }
                },
                {
                  "!jq": "[ .body.resource.custom_questions[] | { id: .name, title: .name, type: \"string\" } ]"
                },
                {
                  "!jq": "([\"start_time\", \"end_time\", \"first_name\", \"last_name\", \"email\", \"event_name\", \"event_id\", \"event_kind\", \"event_duration\", \"event_created_at\", \"assigned_to\", \"location\", \"text_reminder_number\", \"timezone\", \"invitee_created_at\", \"is_reschedule\"] | map({id: ., title: . | split(\"_\") | join(\" \"), type: \"string\"})) + ."
                }
              ]
            }
          },
          "describe_target": {
            "label": "ActiveCampaign",
            "options": {
              "!resource": "ActiveCampaignContact.fields"
            }
          }
        }
      },
      "data_pipeline": {
        "source": {
          "!pipe": [
            {
              "!jq": "(.payload | ({is_reschedule: (if .old_invitee? then .old_invitee else .invitee end | .is_reschedule)}) + (.event_type | { event_name: .name, event_duration: .duration, event_kind: .kind }) + (.event | { event_id: .uuid, assigned_to: .assigned_to, start_time: .start_time, end_time: .end_time, location, event_created_at: .created_at }) + (.invitee | { email, text_reminder_number, first_name: .name | split(\" \") | first, last_name: (.name | split(\" \") as $name | $name - ($name | [first]) | join(\" \")), timezone, invitee_created_at: .created_at, payments: \"\"})) + (.payload.questions_and_answers | map({(.question): .answer}) | add)"
            },
            {
              "!resource": {
                "action": "ActiveCampaignCustomObject.transform",
                "object": "event",
                "data": "${piped_content.0}"
              }
            },
            {
              "!jq": "${piped_content.1}"
            }
          ]
        },
        "target": {
          "!pipe": [
            {
              "!resource": "ActiveCampaignContact"
            },
            {
              "!resource": {
                "action": "ActiveCampaignCustomObject.sync",
                "object": "event",
                "data": {
                  "external_id": "${source_data.piped_content.0.payload.event.uuid}",
                  "fields": "${source_data.piped_content.2}",
                  "relationships": [
                    {
                      "name": "primary-contact",
                      "value": [
                        "${piped_content.1.contact_id}"
                      ]
                    }
                  ]
                }
              }
            }
          ]
        }
      }
    }
  ]
}