Google helpers for Gmail, Calendar, Drive, People, YouTube (including upload), and Analytics.
- Integrations
- gmail
- calendar
- drive
- people
- youtube
- oauth
- multi-account
- License
- MIT
- Published
- August 21, 2026
- Pinned commit
e4cf19e- Rating
- No ratings yet
- Forks
- 0
- Stars
- 0
- Adaptation effort
- —
README
@kentcdodds/google
Intent
Account-aware Google product helpers for Kent's Kody workflows. Centralizes explicit account selection (personal, business, youtube-brand, youtube-plus), OAuth integration routing, shared API error handling, and dry-run guards for Gmail, Calendar, Drive, People, YouTube, and YouTube Analytics. Use cal-com for Cal.com booking pages and lifecycle—not this package.
When To Use
- Read or mutate Gmail, Google Calendar events, Drive files, or People contacts with the correct Google account.
- Reply to a Gmail message with
createReplyDraft, which quotes the original and sets threading headers from the message itself rather than from hand-copied values. - Call YouTube Data or YouTube Analytics APIs on Kent's
main(youtube-brand) orplus(youtube-plus) channel. - Upload or update YouTube videos (including BEPE promo Shorts) via
uploadVideo/updateVideo, fetching media from a URL such as Cloudflare R2. - Verify OAuth connectivity before a workflow touches Google APIs (
smokeTest). - Resolve which Google account alias to pass when a task mentions personal Gmail, Workspace calendar, or a specific YouTube channel.
Required setup
Connect OAuth integrations at /connect/oauth (tokens are managed by Kody—not raw access-token secrets):
| Account alias | OAuth integration | Typical use |
|---|---|---|
personal | google | Personal Gmail, Drive, default YouTube reads |
business | google-business | Workspace Calendar and Gmail (me@kentcdodds.com) |
youtube-brand | google-youtube-brand | Main Kent C. Dodds YouTube channel |
youtube-plus | google-youtube-plus | kentcdodds-plus / BEPE channel |
Every API call requires an explicit account param. Use dryRun: true before Gmail sends or Calendar mutations.
Exports
kody:@kentcdodds/google— package overview andsmokeTest({ account }).kody:@kentcdodds/google/accounts— list and resolve account aliases.kody:@kentcdodds/google/core— low-levelrequestGoogleandgetUserInfo.kody:@kentcdodds/google/gmail— Gmail search, read, attachments, draft, threadedcreateReplyDraftwithcc/bcc, composedcreateDraft/sendMessage, labelmodifyMessage/ensureLabel, send, URL parsing.kody:@kentcdodds/google/calendar— Google Calendar CRUD, ACL, free/busy.kody:@kentcdodds/google/drive— Drive search, metadata, export.kody:@kentcdodds/google/people— People profile, contacts, connections.kody:@kentcdodds/google/youtube— YouTube channels, search, videos, comments, resumableuploadVideo, andupdateVideo.kody:@kentcdodds/google/youtube-upload-video— resumableuploadVideo(package-invocation friendly default export).kody:@kentcdodds/google/youtube-update-video—updateVideo(package-invocation friendly default export).kody:@kentcdodds/google/youtube-analytics— YouTube Analytics reports.kody:@kentcdodds/google/youtube-channels— Kent channel registry (main/plus).kody:@kentcdodds/google/types— shared param/result type metadata.
Example
import { listEvents } from 'kody:@kentcdodds/google/calendar'
export default async function main() {
return await listEvents({ account: 'business', maxResults: 5 })
}Replying to Gmail
createDraft takes a complete raw MIME string and stores it verbatim, so a caller
that assembles its own message produces a reply with no quoted original. Quoting
is a mail-client convention, not something the Gmail API adds.
createReplyDraft closes that gap. Pass the message id being replied to plus the
reply text, and it derives the recipient, subject, In-Reply-To, References,
threadId, and the quoted original from the fetched message. Only body comes
from the caller, so quoted text and threading headers are copied by code instead
of retyped.
import { createReplyDraft } from 'kody:@kentcdodds/google/gmail'
export default async function main() {
return await createReplyDraft({
account: 'business',
replyToMessageId: '19fdd464c417fd87',
body: ['Hi Adam,', 'Thanks for offering to record it.', '- Kent'],
cc: 'sponsor-kcd@10xn.dev',
})
}Replies go out as multipart/related wrapping multipart/alternative, the same
shape mail clients produce. The text/html part nests the original markup in a
gmail_quote blockquote so quoted formatting, links, and screenshots survive,
and inline images the quote references by cid: are re-attached so they still
render. The text/plain part carries the usual > quoting, with existing markers
deepened so nested history survives.
The attribution stamp uses the mailbox's own time zone, read from Calendar
settings, because that is what clients display. Pass timeZone to override it.
Pass cc and bcc as a string or string list when the reply should copy
someone. Those headers are not inferred from the original message. createDraft
and sendMessage accept the same to / cc / bcc / subject / body
fields when you are not assembling raw MIME yourself.
Pass dryRun: true to get back the assembled mime, text, html, resolved
timeZone, and the inlineImages list without creating anything. Non-ASCII
display names are RFC 2047 encoded. Re-attached inline images are capped by
maxInlineImageBytes, and anything skipped is reported as skippedInlineImages.
Kent publishes to two YouTube channels: main (youtube-brand, Better with Kent) and plus (youtube-plus, BEPE / legacy Chats with Kent). Import resolveYouTubeChannel before upload or analytics work.
Report this listing
Log in to report this listing.