SkillAgentSearch skills...

Calendlyr

✨ Ruby bindings for Calendly API.

Install / Use

/learn @araluce/Calendlyr
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

Calendlyr logo

Maintainability Code Coverage Gem Downloads

The simplest way to interact with Calendly's API v2 in Ruby. No runtime dependencies, no ceremony — just a Personal Access Token and you're good to go.

📖 Behind the gem: Curious about the design decisions, trade-offs, and real-world lessons from building and maintaining Calendlyr? I wrote a 5-part series covering everything — from why gems aren't as scary as you think to how this wrapper was built from scratch with zero dependencies.

Installation

Calendlyr requires Ruby >= 3.2.0.

Add to your Gemfile:

gem "calendlyr"

Then run bundle install. That's it.

Quick Start

client = Calendlyr::Client.new(token: ENV["CALENDLY_TOKEN"])

# List your scheduled events — just pass the UUID, no full URI needed
events = client.events.list(user: "YOUR_USER_UUID")
events.data
#=> [#<Calendlyr::Event>, #<Calendlyr::Event>, ...]

# Access event details naturally
event = events.data.first
event.name       #=> "30 Minute Meeting"
event.status      #=> "active"
event.start_time  #=> "2024-01-15T10:00:00.000000Z"

# List invitees for an event
invitees = client.events.list_invitees(uuid: event.uuid)
invitees.data.first.email  #=> "john@example.com"

Global configuration

For single-tenant apps, you can configure Calendlyr once and reuse a default client:

Calendlyr.configure do |config|
  config.token = ENV.fetch("CALENDLY_TOKEN")
  config.open_timeout = 5
  config.read_timeout = 15
end

client = Calendlyr.client
events = client.events.list(user: "YOUR_USER_UUID")

Calendlyr.client memoizes a client instance and rebuilds it if token or timeout values change.

Optional request/response logging

Calendlyr can emit request lifecycle logs with any logger-like object that responds to info, debug, warn, and error. Logging is opt-in, and the gem does not ship a logger implementation for you.

require "logger"

client = Calendlyr::Client.new(token: ENV["CALENDLY_TOKEN"], logger: Logger.new($stdout))

Logger is just an example. You can pass any object that responds to info, debug, warn, and error.

If you're on Ruby 4 and want to use Ruby's Logger, make sure your application includes the logger gem.

Or configure it globally:

Calendlyr.configure do |config|
  config.token = ENV.fetch("CALENDLY_TOKEN")
  config.logger = Logger.new($stdout)
end

In Rails, this is typically configured in an initializer:

# config/initializers/calendlyr.rb
Calendlyr.configure do |config|
  config.token = ENV.fetch("CALENDLY_TOKEN")
end

[!IMPORTANT] Calendlyr.client is module-global and not thread-safe for multi-tenant usage. If your app serves multiple tenants or uses per-request credentials, use Calendlyr::Client.new(token:) per request.

Bare UUIDs

Most methods that take a Calendly resource reference (like user:, organization:, event_type:, or owner:) accept both bare UUIDs and full URIs. When supported, the gem expands bare UUIDs automatically:

# Both are equivalent:
client.events.list(user: "YOUR_USER_UUID")
client.events.list(user: "https://api.calendly.com/users/YOUR_USER_UUID")

The gem mirrors the Calendly API closely, so converting API examples into gem code is straightforward. Responses are wrapped in Ruby objects with dot-access for every field.

Note: A few endpoints still expect a full Calendly URI for specific parameters. When that matters, the resource docs call it out explicitly.

Webhook signature verification

Calendlyr::Webhook (singular) verifies signed webhook payloads. This is separate from client.webhooks / Calendlyr::Webhooks (plural), which are API resources for managing webhook subscriptions.

  • Calendly sends the signature in the Calendly-Webhook-Signature HTTP header.
  • In Rack/Rails, that header is available as HTTP_CALENDLY_WEBHOOK_SIGNATURE.
  • verify! raises on invalid signature/timestamp; valid? returns true/false; parse verifies first, then JSON-parses and wraps the payload.
payload = request.body.read
signature_header = request.get_header("HTTP_CALENDLY_WEBHOOK_SIGNATURE")
signing_key = ENV.fetch("CALENDLY_WEBHOOK_SIGNING_KEY")

if Calendlyr::Webhook.valid?(payload: payload, signature_header: signature_header, signing_key: signing_key)
  webhook = Calendlyr::Webhook.parse(
    payload: payload,
    signature_header: signature_header,
    signing_key: signing_key
  )

  webhook.event   #=> "invitee.created"
  webhook.payload #=> Calendlyr::Webhooks::InviteePayload (for invitee.* events)
                 #=> Calendlyr::Object (for other/unknown events)
end

JSON serialization

All API objects support #to_json for easy serialization (caching, logging, API proxying):

event = client.events.retrieve(uuid: "ABC123")

event.to_json
#=> '{"uri":"https://api.calendly.com/scheduled_events/ABC123","name":"30 Minute Meeting",...}'

# Works with JSON.generate and nested objects
JSON.generate(event)

# Round-trip: parse back into an Object
parsed = Calendlyr::Object.new(JSON.parse(event.to_json))
parsed.name  #=> "30 Minute Meeting"

Note: #to_json and #to_h exclude the internal client reference — only API data is serialized.

Error context

API errors now include the HTTP method and path in the message, and expose structured attributes for debugging:

begin
  client.events.retrieve(uuid: "INVALID_UUID")
rescue Calendlyr::NotFound => error
  error.message       #=> "[Error 404] GET /scheduled_events/INVALID_UUID — Not Found. The resource you requested does not exist."
  error.status        #=> 404
  error.http_method   #=> "GET"
  error.path          #=> "/scheduled_events/INVALID_UUID"
  error.response_body #=> { "title" => "Not Found", "message" => "..." }
end

This makes debugging failed requests much easier without changing existing rescue Calendlyr::Error patterns.

Auto-pagination

Calendlyr supports lazy auto-pagination for paginated collection endpoints. There are two ways to consume paginated results:

list_all — Eager, returns a flat Array

The simplest option. Fetches every page and returns all items as an Array.

# Get all events across all pages (e.g., hundreds of events)
events = client.events.list_all(organization: "YOUR_ORG_UUID")
events  #=> [#<Calendlyr::Event>, #<Calendlyr::Event>, ...]

# Same pattern works for every resource with a list method:
client.event_types.list_all(organization: "YOUR_ORG_UUID")
client.webhooks.list_all(organization: "YOUR_ORG_UUID", scope: "organization")
client.organizations.list_all_memberships(organization: "YOUR_ORG_UUID")
client.organizations.list_all_invitations(uuid: "YOUR_ORG_UUID")
client.organizations.list_all_activity_log(organization: "YOUR_ORG_UUID")
client.groups.list_all(organization: "YOUR_ORG_UUID")
client.groups.list_all_relationships(organization: "YOUR_ORG_UUID")
client.routing_forms.list_all(organization: "YOUR_ORG_UUID")
client.routing_forms.list_all_submissions(form: "YOUR_FORM_UUID")
client.availability.list_all_user_busy_times(user: "YOUR_USER_UUID", start_time: "...", end_time: "...")
client.availability.list_all_user_schedules(user: "YOUR_USER_UUID")
client.locations.list_all
client.event_types.list_all_memberships(event_type: "YOUR_EVENT_TYPE_UUID")
client.outgoing_communications.list_all(organization: "YOUR_ORG_UUID")

auto_paginate — Lazy Enumerator

Returns an Enumerator::Lazy that fetches pages on demand. Pages are only requested as you consume items, so you can stop early without fetching all pages.

collection = client.events.list(organization: "YOUR_ORG_UUID")

# Take only the first 50 events — fetches only as many pages as needed
first_50 = collection.auto_paginate.take(50)

# Filter lazily — stops fetching once the condition is met
active = collection.auto_paginate.select { |e| e.status == "active" }.first(10)

# Consume all items lazily
collection.auto_paginate.each do |event|
  puts event.name
end

Documentation

For the full list of available resources and methods, check out the API Reference.

The docs in this repository focus on the Ruby wrapper API. For request/response schemas and endpoint-level behavior, use the official Calendly API docs linked from each resource page.

Contributing

  1. Fork it
  2. Create your feature branch (git checkout -b my-new-feature)
  3. Commit your changes (git commit -am 'Add some feature')
  4. Push to the branch (git push origin my-new-feature)
  5. Create a new Pull Request

When adding resources, please write tests and update the docs.

View on GitHub
GitHub Stars23
CategoryDevelopment
Updated1d ago
Forks1

Languages

Ruby

Security Score

95/100

Audited on Apr 8, 2026

No findings