Read Calendly event types, scheduled events, and invitees on the caller's account, with dry-run mutations.
- Integrations
- calendly
- calendar
- scheduling
- event-types
- scheduled-events
- invitees
- oauth
- pat
- dry-run
- License
- MIT
- Published
- August 22, 2026
- Pinned commit
0341360- Rating
- No ratings yet
- Forks
- 0
- Stars
- 0
- Adaptation effort
- —
README
@kody/calendly
Intent
Reusable Calendly helpers so Kody agents can inspect the caller's event
types, scheduled events, and invitees — not a shared platform calendar, and not
Cal.com. Auth is a saved personal access token by default, or a bring-your-own
OAuth client. Reads are free-form. Mutations support dryRun: true, and live
writes also require confirm: true.
This listing is meant to be forked. After you fork, save your own calendlyPat
(or connect OAuth) and call the helpers in your account. No personal Calendly
scheduling URLs, user slugs, or organization URIs are hard-coded.
Use @kody/cal-com for Cal.com booking pages, slots, and webhooks. Use a
Google Calendar helpers package for direct Google Calendar event CRUD.
Share this package as https://kody.codes/@kody/calendly
(never a /community/{listing_id} URL).
Auth
Calendly has no built-in Kody OAuth app. Choose one lane:
| Lane | Credential | When to use |
|---|---|---|
| A. Personal access token (fastest) | User secret calendlyPat | Internal / single-account automation. Calendly recommends PATs for private apps. |
| B. Bring-your-own OAuth | Saved integration named calendly (or another name you pass as integrationName) | Multi-account or public apps. There is no built-in Calendly platform app. |
Both lanes send Authorization: Bearer … to https://api.calendly.com. Do not
paste tokens or client secrets into chat.
Lane A: Personal access token
- Create a token at Integrations → API & Webhooks. Grant
users:read,event_types:read,scheduled_events:read, andscheduled_events:writefor the helpers in this package. - Save it in Kody (do not paste the value in chat):
https://kody.codes/account/secrets/new?name=calendlyPat&description=Calendly%20personal%20access%20token%20(Bearer%20token%20for%20api.calendly.com)&allowedHosts=api.calendly.com&scope=user- In the account secrets UI, approve host
api.calendly.com. - Smoke-test with
./smoke-test(see Smoke test).
Pass account: "work" to use secret calendlyPat-work, or pass
secretName: "calendlyPat-work". There are no hard-coded account aliases.
Lane B: OAuth
Create an OAuth app at developer.calendly.com. Register the redirect URI exactly:
https://kody.codes/connect/oauthUse Production for live account data. Calendly shows the client secret only
once. Then open this prefilled connect URL while signed in to Kody and paste
the client ID and client secret into the setup form (flow=confidential plus
S256 PKCE):
https://kody.codes/connect/oauth?provider=calendly&authorizeUrl=https%3A%2F%2Fauth.calendly.com%2Foauth%2Fauthorize&tokenUrl=https%3A%2F%2Fauth.calendly.com%2Foauth%2Ftoken&apiBaseUrl=https%3A%2F%2Fapi.calendly.com&scopes=users%3Aread%20event_types%3Aread%20scheduled_events%3Aread%20scheduled_events%3Awrite&flow=confidential&pkce=true&allowedHosts=api.calendly.com%2Cauth.calendly.com&dashboardUrl=https%3A%2F%2Fdeveloper.calendly.com%2F&providerSetupInstructions=Create%20an%20OAuth%20app%20at%20https%3A%2F%2Fdeveloper.calendly.com%2F.%20Register%20redirect%20URI%20exactly%20https%3A%2F%2Fkody.codes%2Fconnect%2Foauth.%20Use%20Production%20for%20live%20account%20data.%20Calendly%20shows%20the%20client%20secret%20only%20once.%20Paste%20the%20client%20id%20and%20client%20secret%20into%20this%20Kody%20form.%20Request%20scopes%20users%3Aread%20event_types%3Aread%20scheduled_events%3Aread%20scheduled_events%3Awrite.%20Do%20not%20paste%20secrets%20into%20chat.Decoded: authorize https://auth.calendly.com/oauth/authorize, token
https://auth.calendly.com/oauth/token, scopes
users:read event_types:read scheduled_events:read scheduled_events:write,
hosts api.calendly.com,auth.calendly.com.
Call helpers with integrationName: "calendly" (or your chosen provider name).
secretName selects the PAT lane and ignores integrationName unless you also
pass auth: "oauth". When both a calendly integration and calendlyPat
exist, helpers prefer OAuth unless you pass auth: "pat".
Reconnect later at https://kody.codes/connect/oauth?provider=calendly.
To connect a second account, change provider (for example
provider=calendly-work) and pass account: "work" or
integrationName: "calendly-work" / secretName: "calendlyPat-work" on every
call.
Hosts
api.calendly.com— all REST calls (required)auth.calendly.com— OAuth authorize + token (OAuth lane)
Scopes
| Scope | Needed for |
|---|---|
users:read | ./get-user, ./smoke-test |
event_types:read | ./list-event-types, ./get-event-type, ./get-available-times |
scheduled_events:read | ./list-scheduled-events, ./get-scheduled-event, ./list-invitees, ./get-invitee |
scheduled_events:write | ./cancel-scheduled-event, ./create-invitee, ./mark-no-show |
If Calendly returns 401/403 or an insufficient-scope error, helpers throw a message that names the missing scope and the next setup URL.
Mutation safety
Pass dryRun: true on cancel-scheduled-event, create-invitee,
mark-no-show, and mutating request calls to return
{ dryRun: true, wouldCall } without contacting Calendly.
Those same writes also throw unless confirm: true.
import cancelScheduledEvent from 'kody:@kody/calendly/cancel-scheduled-event'
const preview = await cancelScheduledEvent({
event: 'AAAAAAAAAAAAAAAA',
reason: 'Need to reschedule',
dryRun: true,
})
const canceled = await cancelScheduledEvent({
event: 'AAAAAAAAAAAAAAAA',
reason: 'Need to reschedule',
confirm: true,
})Smoke test
Run this from execute after the PAT (or OAuth) is saved. Prefer
packages.invoke so secret mounts and OAuth run in package runtime.
import { packages } from 'kody:runtime'
export default async function main() {
return await packages.invoke({
kodyId: 'calendly',
exportName: './smoke-test',
})
}Pass the bare kody id calendly, not @kody/calendly.
Without calendlyPat (and without a connected calendly integration) this
still returns { ok: true, live: false } plus the setup URLs. With credentials
it reads a trimmed /users/me profile and does not return email.
Dry-run preview (no network):
import calendly from 'kody:@kody/calendly'
export default async function main() {
return await calendly({ dryRun: true })
}Exports
kody:@kody/calendly— action dispatcher (defaults tosmoke-test)kody:@kody/calendly/accounts— resolve integration/secret names and setup URLskody:@kody/calendly/smoke-test— credential smoke test (read-only)kody:@kody/calendly/get-user— authorizing user (read-only, no email)kody:@kody/calendly/list-event-types— list event types (read-only)kody:@kody/calendly/get-event-type— event type detail (read-only)kody:@kody/calendly/get-available-times— bookable start times (read-only)kody:@kody/calendly/list-scheduled-events— list scheduled events (read-only)kody:@kody/calendly/get-scheduled-event— scheduled event detail (read-only)kody:@kody/calendly/cancel-scheduled-event— cancel an event (dryRun/confirm)kody:@kody/calendly/list-invitees— list invitees (read-only)kody:@kody/calendly/get-invitee— invitee detail (read-only)kody:@kody/calendly/create-invitee— book a time (dryRun/confirm)kody:@kody/calendly/mark-no-show— mark an invitee no-show (dryRun/confirm)kody:@kody/calendly/request— low-level Calendly API escape hatchkody:@kody/calendly/types— shared TypeScript types
Examples
import listEventTypes from 'kody:@kody/calendly/list-event-types'
export default async function main() {
return await listEventTypes({ active: true, count: 20 })
}import listScheduledEvents from 'kody:@kody/calendly/list-scheduled-events'
import listInvitees from 'kody:@kody/calendly/list-invitees'
export default async function main() {
const { items } = await listScheduledEvents({ status: 'active', count: 10 })
if (!items[0]) return { items, invitees: [] }
return await listInvitees({ event: items[0].uuid })
}import createInvitee from 'kody:@kody/calendly/create-invitee'
export default async function main() {
return await createInvitee({
eventType: 'AAAAAAAAAAAAAAAA',
startTime: '2026-06-01T16:00:00Z',
invitee: { name: 'Ada', email: 'ada@example.com', timezone: 'America/Denver' },
dryRun: true,
})
}Notes
- REST base:
https://api.calendly.com - List helpers return
{ items, pageInfo }. Get helpers return a slim resource. - Uuids may be bare or full
https://api.calendly.com/...URIs. POST /invitees(create-invitee) can require a paid Calendly plan.- This package is not affiliated with or endorsed by Calendly.
Branding
community-icon.svg is Calendly's official brand mark (the C-in-circle logomark
from Calendly's public media kit, color #006BFF). Paths are unmodified except
for a square tile so the mark stays legible at 56 pixels. Calendly® is a
trademark of Calendly. This package is not affiliated with or endorsed by
Calendly.
Docs
Report this listing
Log in to report this listing.